提到弹幕类游戏,想到的最多的应该是《东方Project》系列了吧
酷炫的弹幕酷炫的弹幕我只记得酷炫。。。。
今天我们也来实现一个仿制版,阉割版~
这个版本的目的是简单的理解游戏的基本实现,所以很多地方会出现写死或者设计不合理的
关于弹幕类游戏,有篇文章写的不错。我也就不重复写了
今天首先来实现以下辅助库,因为这些作为游戏的基石,还是很重要的
由于C#没有原生的Vector类(XNA有,但这里不打算用XNA)所以我们自己实现一个
public struct Vector2
{
public static readonly Vector2 Zero = new Vector2(0, 0);
public float X;
public float Y;
public Vector2(float x, float y)
{
this.X = x;
this.Y = y;
}
public Vector2(Vector2 v)
{
this.X = v.X;
this.Y = v.Y;
}
public override string ToString()
{
return "(" + X.ToString() + "," + Y.ToString() + ")";
}
public override bool Equals(object obj)
{
if (object.Equals(obj, null))
{
return false;
}
Vector2 v = (Vector2)obj;
return this.X == v.X && this.Y == v.Y;
}
public override int GetHashCode()
{
return X.GetHashCode() + Y.GetHashCode();
}
public void Normalize()
{
float m = this.X * this.X + this.Y * this.Y;
if (m < 0.00001f)
{
m = 1;
}
m = 1.0f / (float)Math.Sqrt((double)m);
X = X * m;
Y = Y * m;
}
public void Reverse()
{
this.X = -this.X;
this.Y = -this.Y;
}
public float Length()
{
return (float)Math.Sqrt(X * X + Y * Y);
}
public float Angle()
{
return (float)Math.Atan2(Y, X);
}
public float Angle(Vector2 vec)
{
Vector2 s = this;
s.Normalize();
vec.Normalize();
return (float)Math.Acos(Vector2.Dot(s, vec));
}
public void Rotate(float angle)
{
float x = X;
float y = Y;
x = X * (float)Math.Cos(angle) - Y * (float)Math.Sin(angle);
y = X * (float)Math.Sin(angle) + Y * (float)Math.Cos(angle);
X = x;
Y = y;
}
public static bool operator ==(Vector2 v1, Vector2 v2)
{
if (object.Equals(v1, null) || object.Equals(v2, null))
{
return object.Equals(v1, v2);
}
if (v1.X == v2.X)
{
return v1.Y == v2.Y;
}
return false;
}
public static bool operator !=(Vector2 v1, Vector2 v2)
{
if (object.Equals(v1, null) || object.Equals(v2, null))
{
return !object.Equals(v1, v2);
}
if (v1.X != v2.X || v1.Y != v2.Y)
{
return true;
}
return false;
}
public static Vector2 operator +(Vector2 v1, Vector2 v2)
{
return new Vector2(v1.X + v2.X, v1.Y + v2.Y);
}
public static Vector2 operator -(Vector2 v1, Vector2 v2)
{
return new Vector2(v1.X - v2.X, v1.Y - v2.Y);
}
public static Vector2 operator *(Vector2 v1, Vector2 v2)
{
return new Vector2(v1.X * v2.X, v1.Y * v2.Y);
}
public static Vector2 operator *(Vector2 v, float s)
{
return new Vector2(v.X * s, v.Y * s);
}
public static Vector2 operator *(float s, Vector2 v)
{
return new Vector2(v.X * s, v.Y * s);
}
public static Vector2 operator /(Vector2 v, float s)
{
if (Math.Abs(s) < float.MinValue)
{
return Vector2.Zero;
}
return new Vector2(v.X / s, v.Y / s);
}
public static Vector2 operator /(float s, Vector2 v)
{
if (Math.Abs(s) < float.MinValue)
{
return Vector2.Zero;
}
return new Vector2(v.X / s, v.Y / s);
}
public static Vector2 operator -(Vector2 v)
{
Vector2 vec;
vec.X = -v.X;
vec.Y = -v.Y;
return vec;
}
public static float Distance(Vector2 v1, Vector2 v2)
{
float x = v1.X - v2.X;
float y = v1.Y - v2.Y;
float total = x * x + y * y;
return (float)Math.Sqrt((double)total);
}
public static float Dot(Vector2 v1, Vector2 v2)
{
return v1.X * v2.X + v1.Y * v2.Y;
}
public static Vector2 Lerp(Vector2 min, Vector2 max, float value)
{
Vector2 v;
v.X = min.X + (max.X - min.X) * value;
v.Y = min.Y + (max.Y - min.Y) * value;
return v;
}
}
很简单,相应了Vector3和Vector4的实现没有给出,大体上一样的
另外,辅助库也很重要,因为子弹的运行轨迹,大多数需要数学公式进行计算的
public static class Helper
{
private static float[] mSinValue;
private static float[] mCosValue;
static Helper()
{
mSinValue = new float[360];
mCosValue = new float[360];
for ( float i = 0; i < 360.0f; i++ )
{
mSinValue[(int)i] = (float)Math.Sin(i * Data.Radian);
mCosValue[(int)i] = (float)Math.Cos(i * Data.Radian);
}
}
public static float FastSin(int angle)
{
angle = angle % 360;
if ( angle < 0 )
{
angle = -angle;
return -mSinValue[angle];
}
return mSinValue[angle];
}
public static float FastCos(int angle)
{
angle = angle % 360;
if ( angle < 0 )
{
angle = -angle;
}
return mCosValue[angle];
}
public static float Clamp(float value, float min, float max)
{
if ( value < min )
{
return min;
}
if ( value > max )
{
return max;
}
return value;
}
public static float GetRandomFloat(float min, float max)
{
return min + (float)Data.Rnd.NextDouble() * (max - min);
}
public static bool GetRandomBool()
{
return Data.Rnd.NextDouble() <= 0.5f;
}
public static int GetRandomInt(int min, int max)
{
return Data.Rnd.Next(min, max);
}
public static Vector2 GetRandomPosition(float left, float right, float top, float bottom)
{
return new Vector2(GetRandomFloat(left, right), GetRandomFloat(top, bottom));
}
public static Vector2 GetRandomVector(float xMin, float xMax, float yMin, float yMax)
{
return new Vector2(GetRandomFloat(xMin, xMax), GetRandomFloat(yMin, yMax));
}
public static Vector2 GetSpeedWithAngle(int angle, float speed)
{
float x = Helper.FastCos(angle) * speed;
float y = Helper.FastSin(angle) * speed;
return new Vector2(x, y);
}
public static Vector2 GetSpeedWithPosition(Vector2 start, Vector2 end, float speed)
{
Vector2 result = (end - start);
result.Normalize();
return result * speed;
}
}
还有一个类似于XNA中Rectangle的类,用于做碰撞盒的
/// <summary>
/// 矩形盒子
/// </summary>
public struct Box
{
public static readonly Box Zero = new Box(0, 0, 0, 0);
private int mX;
private int mY;
private int mWidth;
private int mHeight;
private int mHalfWidth;
private int mHalfHeight;
public int X
{
get
{
return mX;
}
set
{
mX = value;
}
}
public int Y
{
get
{
return mY;
}
set
{
mY = value;
}
}
public int Width
{
get
{
return mWidth;
}
set
{
mWidth = value;
mHalfWidth = value / 2;
}
}
public int Height
{
get
{
return mHeight;
}
set
{
mHeight = value;
mHalfHeight = value / 2;
}
}
public int Left
{
get
{
return mX - mHalfWidth;
}
}
public int Right
{
get
{
return mX + mHalfWidth;
}
}
public int Top
{
get
{
return mY - mHalfHeight;
}
}
public int Bottom
{
get
{
return mY + mHalfHeight;
}
}
public Box(int x, int y, int width, int height)
{
this.mX = x;
this.mY = y;
this.mWidth = width;
this.mHeight = height;
this.mHalfWidth = width / 2;
this.mHalfHeight = height / 2;
}
public Box(Box v)
{
this.mX = v.mX;
this.mY = v.mY;
this.mWidth = v.mWidth;
this.mHeight = v.mHeight;
this.mHalfWidth = v.mHalfWidth;
this.mHalfHeight = v.mHalfHeight;
}
public override string ToString()
{
return "(" + mX.ToString() + "," + mY.ToString() + "," + mWidth.ToString() + "," + mHeight.ToString() + ")";
}
public Rectangle ToRectangle()
{
return new Rectangle(this.mX - mHalfWidth, this.mY - mHalfHeight, this.mWidth, this.mHeight);
}
public override bool Equals(object obj)
{
if (object.Equals(obj, null))
{
return false;
}
Box v = (Box)obj;
return this.mX == v.mX && this.mY == v.mY && this.mWidth == v.mWidth && this.mHeight == v.mHeight;
}
public override int GetHashCode()
{
return mX.GetHashCode() + mY.GetHashCode() + mWidth.GetHashCode() + mHeight.GetHashCode();
}
public static bool operator ==(Box v1, Box v2)
{
if (object.Equals(v1, null) || object.Equals(v2, null))
{
return object.Equals(v1, v2);
}
if (v1.mX == v2.mX)
{
return v1.mY == v2.mY && v1.mWidth == v2.mHeight && v1.mHeight == v2.mHeight;
}
return false;
}
public static bool operator !=(Box v1, Box v2)
{
if (object.Equals(v1, null) || object.Equals(v2, null))
{
return !object.Equals(v1, v2);
}
if (v1.mX != v2.mX || v1.mY != v2.mY || v1.mWidth != v2.mWidth || v1.Height != v2.mHeight)
{
return true;
}
return false;
}
public bool Contains(int x, int y)
{
if (Math.Abs(x - mX) < mHalfWidth && Math.Abs(y - mY) < mHalfHeight)
{
return true;
}
return false;
}
public bool Contains(GPoint p)
{
return Contains(p.X, p.Y);
}
public bool Collide(Box box)
{
if (this.Left > box.Right || this.Right < box.Left)
{
return false;
}
if (this.Top > box.Bottom || this.Bottom < box.Top)
{
return false;
}
return true;
}
public static bool Collide(Box box1, Box box2)
{
return box1.Collide(box2);
}
}
大体上用的比较多的就是这三个类了,其余的都不算重要
而且这三个类很简单,不需要太多的说明,下一章开始进入游戏逻辑代码