Tuesday, July 26, 2011
Noir Consulting - really?
It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level.
del "$(ProjectDir)\obj\Debug\Package\PackageTmp\web.config"
Friday, May 27, 2011
If you install Azure SDK 1.4 and then your web roles say System.TypeLoadException for System.ServiceModel.Activation.HttpModule in ASP.NET 4
Thursday, May 26, 2011
The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable'.
Saturday, May 21, 2011
A value shared across entities or associations is generated in more than one location.
A value shared across entities or associations is generated in more than one location.
Thursday, March 31, 2011
Linq Dreams - GroupAndAggregate
IEnumerable<KeyValuePair<TKey, TOutput>>
GroupAndAggregate<TElement, TKey, TOutput>
(this IEnumerable<TElement> source, Func<TElement, TKey> keyMapping,
Func<TElement, TOutput> firstElementMapping,
Func<TElement, TOutput, TOutput> subsequentElementMapping)
{
Dictionary<TKey, TOutput> dictionary = new Dictionary<TKey, TOutput>();
foreach (TElement element in source)
{
TKey key = keyMapping(element);
TOutput output = default(TOutput);
if (dictionary.TryGetValue(key, out output))
{
output = subsequentElementMapping(element, output);
}
else
{
output = firstElementMapping(element);
}
dictionary[key] = output;
}
return dictionary;
}
Wednesday, March 23, 2011
IronRuby is alive and kicking!
Monday, March 21, 2011
Help fund ironruby wp7 development - vote for iron7 in WP7Comp
- go to iron7 on WP7Comp and leave a LIKE and a positive comment.
Monday, March 14, 2011
WP7 - the effect of being a recommended app
Saturday, March 05, 2011
Wednesday, March 02, 2011
WP7 apps that developers love... WP7Comp
Thursday, February 10, 2011
DDDHack - XNA mods
Some #DDDHack Action
public class WrappedAccelerometer
{
static Accelerometer accelerometer;
static AccelerometerReadingEventArgs lastSeenAcceleration;
static WrappedAccelerometer()
{
accelerometer = new Accelerometer();
accelerometer.Start();
accelerometer.ReadingChanged += new System.EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);
lastSeenAcceleration = null;
}
static void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
lastSeenAcceleration = e;
}
public static AccelerometerReadingEventArgs GetState()
{
return lastSeenAcceleration;
}
}
public static class VirtualThumbsticks
{
static VirtualThumbsticks()
{
}
/// <summary>
/// Gets the value for steering.
/// </summary>
public static double Steering
{
get
{
var accelerometerState = WrappedAccelerometer.GetState();
// if there is no accelerometer state, then return a value of 0.0
if (null == accelerometerState)
return 0.0;
// because we are in landscape...
// we have to use accerometer state
return accelerometerState.Y;
}
}
/// <summary>
/// Gets the value for acceleration.
/// </summary>
public static double Thrusters
{
get
{
var accelerometerState = WrappedAccelerometer.GetState();
// if there is no accelerometer state, then return a value of 0.0
if (null == accelerometerState)
return 0.0;
// calculate the scaled vector from the touch position to the center,
// scaled by the maximum thumbstick distance
// this conversions sets normalised from the Z value:
// accelerometerState.Z will be -1.0 when the phone is flat down
// accelerometerState.Z will be 0.0 when the phone is upright
// we convert that to thrust ratio using:
var thrust = -2.0 * accelerometerState.Z - 1.0;
if (thrust > 1.0)
thrust = 1.0;
if (thrust < -1.0)
thrust = -1.0;
// this actually works quite nicely thanks to trigonomics :)
//System.Diagnostics.Debug.WriteLine(string.Format("Z is {0:0.00}, N is {1:0.00}", accelerometerState.Z, thrust));
return thrust;
}
}
/// <summary>
/// Updates the virtual thumbsticks based on current touch state. This must be called every frame.
/// </summary>
public static void Update()
{
// do nothing for now
}
}
// Determine rotation amount from input
rotationAmount.X = (float)VirtualThumbsticks.Steering;
// Scale rotation amount to radians per second
rotationAmount = rotationAmount * RotationRate * elapsed;
// ...
// Determine thrust amount from input
float thrustAmount = (float)VirtualThumbsticks.Thrusters;
// Calculate force from thrust amount
Vector3 force = Direction * thrustAmount * ThrustForce;
// Create rotation matrix from rotation amount
Matrix rotationMatrix =
Matrix.CreateFromAxisAngle(Right, rotationAmount.Y) *
Matrix.CreateRotationY(rotationAmount.X) *
Matrix.CreateRotationX(rotationAmount.Z);
TouchCollection touches = TouchPanel.GetState();
touchActive = touches.Count;
public static double PitchAcceleration
{
get
{
if (touchActive)
return 1.0;
return -1.0;
}
}
// modify the Altitude
Position.Y += AltitudeStep * (float)VirtualThumbsticks.PitchAcceleration;
4. To provide some variation in height, set some constants in Ship.cs:
private const float MinimumAltitude = 100.0f;
private const float MaximumAltitude = 6000.0f;
private const float AltitudeStep = 50.0f;
5. To make the environment a bit more interesting, change the altitude values of our prizes and enemy ships in the main game file. e.g.
// Two arrays that define the location of obstacles and collection items.
// these could be loaded per level giving different level designs
Vector3[] coords = new Vector3[] { new Vector3(45000, 50, 57000),
new Vector3(-45000, 2000, 57000),
new Vector3(57000, 1500, -45000),
new Vector3(-57000, 4000, -45000),
new Vector3(8000f, 3000, 0f),
new Vector3(-8000f, 2000, 0f),
new Vector3(21500, 1000, 32500),
new Vector3(-21500, 3000, 21500),
new Vector3(21500, 2000, -21500),
new Vector3(-21500, 400, -21500) };
Vector3[] treecoords = new Vector3[] { new Vector3(-18000f, 1200f, 0f),
new Vector3(0f, 2400f, 18000f),
new Vector3(0f, 3600f, -18000f),
new Vector3(57000f, 1200f, 0f),
new Vector3(-57000f, 2400f, 0f),
new Vector3(0f, 3600f, 57000f),
new Vector3(0f, 1200f, -57000f),
new Vector3(32500f, 2400f, 32500f),
new Vector3(-32500f, 3600f, 32500f),
new Vector3(32500f, 1200f, -32500f),
new Vector3(-32500f, 1200f, -32500f) };:
So the game's working... but it's a bit static... so next up was to hack into the main game loop and to give the enemies some motion...
To do this, I took each existing Enemy tree (currently just a sphere) and replaced them with an object:
public class EnemyShip
{
private static float MaxSpeed = 80.0f;
private static float AccelerationConstant = 10.0f;
public BoundingSphere Sphere;
public BoundingSphere OriginalSphere;
public Vector3 CurrentSpeed;
public EnemyShip(BoundingSphere sphere)
{
OriginalSphere = sphere;
Reset();
}
public void Reset()
{
Sphere = new BoundingSphere(OriginalSphere.Center, OriginalSphere.Radius);
CurrentSpeed = new Vector3();
}
public void AccelerateTowards(Vector3 Position)
{
var accerlationDirection = Position - Sphere.Center;
accerlationDirection.Normalize();
CurrentSpeed += AccelerationConstant * accerlationDirection;
var currentLength = CurrentSpeed.Length();
if (currentLength > MaxSpeed)
{
CurrentSpeed = CurrentSpeed * MaxSpeed / currentLength;
}
}
public void UpdatePosition()
{
Sphere.Center += CurrentSpeed;
}
}
then each time around the loop, I call AccelerateTowards() and UpdatePosition on each of these enemies.
Result...
- the enemies definitely come chase
- the enemies also quite often collide with each other - I should add some sort of protection to stop them overlapping...
- when you crash into an enemy it's also quite hard to escape again afterwards!
To cope with the overlapping... I added System.Linq, changed the update to
public void UpdatePosition(System.Collections.Generic.IEnumerable<BoundingSphere> DoNotMoveWithin)
{
var enlargedSphere = new BoundingSphere(Sphere.Center + CurrentSpeed, Sphere.Radius * 3);
if (DoNotMoveWithin.Any(x => x.Intersects(enlargedSphere)))
return;
var candidateNewSphere = new BoundingSphere(Sphere.Center + CurrentSpeed, Sphere.Radius);
Sphere = candidateNewSphere;
}
and called this using:
enemyShip.UpdatePosition(enemyShips.Where(x => x != enemyShip).Select(x => x.Sphere));
This kind of works.... but it also leads to some problems - e.g. once the enemy ships have moved together they don't move apart again.
I guess I could add some code that means each enemy has to stay within X of its starting location.... another day... another hack...
public void UpdatePosition(System.Collections.Generic.IEnumerable<BoundingSphere> DoNotMoveWithin)
{
var newCenter = Sphere.Center + CurrentSpeed;
if ((newCenter - OriginalSphere.Center).Length() > MaxDistanceFromOriginal)
return;
var enlargedSphere = new BoundingSphere(newCenter, Sphere.Radius * 3);
if (DoNotMoveWithin.Any(x => x.Intersects(enlargedSphere)))
return;
var candidateNewSphere = new BoundingSphere(newCenter, Sphere.Radius);
Sphere = candidateNewSphere;
}
Out of time
So that's it... out of time.
What have I learnt from my hack?
- XNA's pretty easy to pick up - it's C# and .Net - you can use Linq :)
- The contents projects look easy - but there's some gotchas in there when importing models - I'm sure if I play with these some more, then they will just work.
- The 3D and Vector code in XNA is *lovely* - it's great to just be able to write code that adds and subtracts Positions and Vectors - and the Matrix code for Rotation is likewise very clean to read - love it.
- Making a 3D model that works nicely on the screen takes work and thought!
- Making a "fly-through" model playable is difficult - even with my "helicopter game" shortcut, then the spaceship was still sometimes hard to control. From a playability perspective, 2D still has lots of advantages which might be why games like Annoyed Avians are selling so well.
- Using the accelerometer in XNA is easy - and it does provide a very immersive experience for a gamer
- Using "virtual buttons" on the screen is also pretty easy in XNA - but user feedback for these buttons is definitely needed...
- The processors on WP7 phones (CPU and GPU) are stunningly quick - the amount of maths going on and the speed its done at is simply awesome.
- I don't think I day gos by when I don't love using Linq - it's magic too.
- I've still got plenty more to learn.... and plenty more hacking to do...
If anyone wants my code then I'll post it somewhere for you all to enjoy :)