Another enemy for the player. Variation of a red daemon.
Thursday, 27 September 2012
Wednesday, 5 September 2012
Tuesday, 4 September 2012
Vertex Shader Outlines
During the development process I needed to have a way to mark an item/enemy with a border. Initially I wanted every 3D object on the scene to have such a glowing border, then decided to give the border to specific objects only when a special ability was in effect (e.g. temporary invulnerability).
My way of achieving this effect was to render whole model scaled up in the background, and then render the normal model. Standard scaling via scale matrix wouldn't work well in this case, so I had to write a vertex shader that would move every vertex of a mesh slightly along its Normal vector. This makes the model look like it's been inflated which is perfect for border/stroke effect. The important thing is to inflate the model after all transformations have been applied (move, rotation, scale, and bone transforms) which means that it needs to be done in world/scene space.
Here's the code for the Stroke Effect shaders.
struct VertexShaderInput
{
float4 Position : POSITION0;
float3 Normal : NORMAL0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
};
VertexShaderOutput VSShadowFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float4 worldPosition = mul(input.Position, World);
worldPosition += (normalize(mul(input.Normal, World)) * 0.02 );
worldPosition.y = 0.05;
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
return output;
}
Monday, 3 September 2012
Coordinates in mixed 3D/2D space
WizardBlast seems to be a legit 3D space game when you look at it, but when you start thinking about the in-game logic it turns out to be 2D limited.
In fact WizardBlast is a 3D rendered 2D game, because everything happens on 2D platform (maze). It is the representation of the maze that gets to full 3D.
In such mixed environment some of the variables regarding position of an in-game entity need to be 2D (x,y), some need to be 3D (x,y,z), and some need both. Also the 3D y component doesn't necessarily need to be equal to the 2D y component.
When I started coding WizardBlast I used Vector3 and Vector2 to store coordinates, and then worked on one of them when needed. It all made the code quite messy and difficult to read/modify/refactor.
That is why I created a new class to store complex position vectors that work in an unificated way. At any time I am able to get both 2D and 3D components and any modification made to one them affects the other.
I think it's a piece of code worth sharing. Not because it's difficult to write, but because it's not always obvious that you need such code :-)
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
Microsoft.Xna.Framework;
namespace
WizardBlast.Entities
{
public
class
Position
{
public
static
float
ZeroFieldXLocation = -2.5f;
public
static
float
ZeroFieldZLocation = -2.5f;
public
static
float
Rift = .55f;
public
static
float
InvRift = 1 / Rift;
private
float
z;
private
float
x;
private
int
ix;
private
int
iz;
public
float
Z { get
{ return
z; } set
{ z = value;
iz = FieldZIndex(value);
} }
public
float
Y { get;
set;
}
public
float
X { get
{ return
x; } set
{ x = value;
ix = FieldXIndex(value);
} }
public
int
IX { get
{ return
ix; } set
{ ix = value;
x = FieldXPosition(value);
} }
public
int
IZ { get
{ return
iz; } set
{ iz = value;
z = FieldZPosition(value);
} }
public
Position(Vector3
position)
{
Z
= position.Z;
Y
= position.Y;
X
= position.X;
}
public
Position(Position
position)
{
IZ
= position.IZ;
Y
= position.Y;
IX
= position.IX;
}
public
Position(int
x, int
z)
{
IZ
= z;
IX
= x;
Y
= 0f;
}
public
Position(float
x, float
z)
{
X
= x;
Z
= z;
Y
= 0f;
}
public
static
Position
operator
+(Position
p1, Position
p2)
{
var
result = new
Position(p1);
result.X
= p1.X + p2.X;
result.Y
= p1.Y + p2.Y;
result.Z
= p1.Z + p2.Z;
return
result;
}
public
static
bool
operator
==(Position
p1, Position
p2)
{
return
p1.ix == p2.ix && p1.iz == p2.iz;
}
public
static
bool
operator
!=(Position
p1, Position
p2)
{
return
p1.ix != p2.ix || p1.iz != p2.iz;
}
//Always
override GetHashCode(),Equals when overloading ==
public
override
bool
Equals(object
o)
{
return
this
== (Position)o;
}
public
override
int
GetHashCode()
{
return
ix ^ iz;
}
public
static
Position
operator
+(Position
p1, Vector3
p2)
{
var
result = new
Position(p1);
result.X
= p1.X + p2.X;
result.Y
= p1.Y + p2.Y;
result.Z
= p1.Z + p2.Z;
return
result;
}
public
static
Position
operator
+(Position
p1, Vector2
p2)
{
var
result = new
Position(p1);
result.X
= p1.X + p2.X;
result.Z
= p1.Z + p2.Y;
return
result;
}
public
static
Position
operator
-(Position
p1, Position
p2)
{
var
result = new
Position(p1);
result.X
= p1.X - p2.X;
result.Y
= p1.Y - p2.Y;
result.Z
= p1.Z - p2.Z;
return
result;
}
public
static
Position
operator
*(Position
p1, float
m)
{
var
result = new
Position(p1);
result.X
= p1.X * m;
result.Y
= p1.Y * m;
result.Z
= p1.Z * m;
return
result;
}
public
static
implicit
operator
Vector3(Position
pos)
{
return
new
Vector3(pos.X,
pos.Y, pos.Z);
}
public
static
implicit
operator
Vector2(Position
pos)
{
return
new
Vector2(pos.X,
pos.Z);
}
public
static
explicit
operator
Position(Vector3
vec)
{
return
new
Position(vec);
}
public
static
explicit
operator
Position(Vector2
vec)
{
return
new
Position((int)vec.X,
(int)vec.Y);
}
private
float
FieldXPosition(int
index)
{
return
ZeroFieldXLocation + index * Rift;
}
private
float
FieldZPosition(int
index)
{
return
ZeroFieldZLocation + index * Rift;
}
private
int
FieldXIndex(float
position)
{
return
Convert.ToInt32((position
- ZeroFieldXLocation) * InvRift);
}
private
int
FieldZIndex(float
position)
{
return
Convert.ToInt32((position
- ZeroFieldZLocation) * InvRift);
}
}
}
Sunday, 2 September 2012
Flying pumpkin
A typical arcade game consists of three main components:
- player
- level
- enemies
Let me present you with one of the enemies from WizardBlast, the Flying Pumkin.
Pumpkin is the easiest to defeat type of enemy you will meet in the game (and the most stupid one). All it does, is wander around in a slow fashion.
These are AI parameters for this peculiar monster:
<Lazyness>0.9</Lazyness>
<TurnSpeed>6.5</TurnSpeed>
<Predictability>2</Predictability>
<Speed>0.35</Speed>
<Intelligent>0</Intelligent>
Subscribe to:
Posts (Atom)