XNA开发思考之二开火吧坦克!
此为原创,转载请注明作者和出处,谢谢!
坦克今天终于开火了,首先附上视频好做展示:http://v.youku.com/v_show/id_XOTkzNDQ5ODg=.html
下面就tank开火简单做说明!
首先说到tank开火就会有击中敌人带有爆炸效果,而爆炸效果可以在前面有介绍,一副图片上有一系爆炸瞬时的效果,
把它们一祯祯绘制出来,连续播放就会有爆炸的效果!具体的代码在游戏源代码下载中,这就就不做介绍了。这里主要介绍
炮弹飞行和如何检测到碰撞敌方目标。炮弹的飞行和tank的运行原理是一样的,只不过炮弹多了一个标志是否存活的bool型的
判断,如果炮弹存活这继续飞行,如果击中目标则死亡消失,不在绘制它。同时又可以打下一发炮弹。
其中GameObject类在源代码中有说明这里就不在解释了。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
for (int i = 0; i < 10; i++) //TANK有十发炮弹可打
{
GameObject ball = new GameObject();
ball.sprite=Content.Load<Texture2D>(@"Pic\bullet");
ball.alive = false;
tankBullet.Add(ball);
}
这是写在LoadContent()中的,意思也不加载10发炮弹,当然数量也可以修改。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
private void KeyboardControl() //响应键盘输入
{
KeyboardState keyboard = Keyboard.GetState(); //获得键盘对象
Vector2 spriteSpeedAdd = new Vector2(5.0f,5.0f); //使坦克加速
if (keyboard.IsKeyDown(Keys.Escape))
{
this.Exit();
}
if (keyboard.IsKeyDown(Keys.Left) || keyboard.IsKeyDown(Keys.A))
{
tank_rotation -= 0.1f;
}
if (keyboard.IsKeyDown(Keys.Right) || keyboard.IsKeyDown(Keys.D))
{
tank_rotation += 0.1f;
}
if (keyboard.IsKeyDown(Keys.W)||keyboard.IsKeyDown(Keys.Up))
{
spriteSpeedAdd.X = (spriteSpeedAdd.X) * ((float)Math.Cos(tank_rotation));
spriteSpeedAdd.Y = (spriteSpeedAdd.Y) * ((float)Math.Sin(tank_rotation));
spritePosition.X-=spriteSpeedAdd.X;
spritePosition.Y -= spriteSpeedAdd.Y;
//spritePosition.X -= (float)Math.Cos(tank_rotation);
//spritePosition.Y -= (float)Math.Sin(tank_rotation);
}
if (keyboard.IsKeyDown(Keys.Down) || keyboard.IsKeyDown(Keys.S))
{
spritePosition.X += (float)Math.Cos(tank_rotation);
spritePosition.Y += (float)Math.Sin(tank_rotation);
}
//tank_rotation = MathHelper.Clamp(tank_rotation, -MathHelper.PiOver2, 0); //角度的旋转范围
if (keyboard.IsKeyDown(Keys.Space)&& !prekeyboard.IsKeyDown(Keys.Space)) //按SPACE发射炮弹
{
foreach (var ball in tankBullet)
{
if (ball.alive == false) //如果有没有使用的炮弹则使用
{
ball.alive = true;
//ball.position = new Vector2(spritePosition.X+tank.Width/2,spritePosition.Y+tank.Height/2);
ball.position = new Vector2(spritePosition.X,spritePosition.Y); //炮弹的其始位置
ball.velocity = new Vector2((float)Math.Cos(tank_rotation)*10,(float)Math.Sin(tank_rotation)*10); //炮弹的移动的X,Y轴的偏移量
}
}
}
}
private void InRegion() //保持TANK在区域中
{
int MaxX = graphics.GraphicsDevice.Viewport.Width - tank.Width;
int MinX = 0;
int MaxY = graphics.GraphicsDevice.Viewport.Height - tank.Height;
int MinY = 0;
//判断X的边界
if (spritePosition.X > MaxX)
{
spritePosition.X = MaxX;
}
else if (spritePosition.X < MinX)
{
spritePosition.X = MinX;
}
//判断Y的边界
if (spritePosition.Y > MaxY)
{
spritePosition.Y = MaxY;
}
else if (spritePosition.Y < MinY)
{
spritePosition.Y = MinY;
}
}
private void UpdateExplosoin() //炸弹爆炸的效果制作
{
foreach (var ex in Explosions)
{
if (ex.alive == true)
{
ex.sourRect = new Rectangle(ex.currentExplosionX * explosionWith, ex.currentExplosionY * explosionHeight, explosionWith, explosionHeight);
ex.currentExplosionX++;
if (ex.currentExplosionX >= explosionSize.X)
{
ex.currentExplosionX = 0;
ex.currentExplosionY++;
if (ex.currentExplosionY >= explosionSize.Y)
{
ex.currentExplosionY = 0;
ex.alive = false;
}
}
}
}
}
static bool IntersectPixels(Rectangle rectangleA,Color[] dataA,Rectangle rectangleB,Color[] dataB) //冲撞判断
{
//比较判断
int top = Math.Max(rectangleA.Top,rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom,rectangleB.Bottom);
int left = Math.Max(rectangleA.Left,rectangleB.Left);
int right = Math.Min(rectangleA.Right,rectangleB.Right);
//判断是否有边框相撞
for (int y = top; y < bottom;y++ )
{
for (int x = left; x < right; x++)
{
Color colorA=dataA[(x-rectangleA.Left)+(y-rectangleA.Top)*rectangleA.Width];
Color colorB=dataB[(x-rectangleB.Left)+(y-rectangleB.Top)*rectangleB.Width];
if (colorA.A != 0 && colorB.A != 0)
{
return true;
}
}
}
return false;
}
public void tankHit() //判断敌车是否和TANK相撞
{
if (random.NextDouble() < enemyProbability) // 敌车出现的位置
{
float x = (float)random.NextDouble() * (Window.ClientBounds.Width - enemy.Width);
enemys.Add(new Vector2(x, -enemy.Height)); //利用随机数添加敌车的数量同时含有其始坐标
}
//tank当前所占的区域
Rectangle tankRectangle = new Rectangle((int)spritePosition.X, (int)spritePosition.Y, tank.Width, tank.Height);
personHit = false;
//所有的敌车都垂直下落,其下落的距离叠带相加
for (int i = 0; i < enemys.Count; i++)
{
//敌车动态效果
enemys[i] = new Vector2(enemys[i].X, enemys[i].Y + enemyFallSpeed);
//画出敌车的矩形
Rectangle enemyRectangle = new Rectangle((int)enemys[i].X, (int)enemys[i].Y, enemy.Width, enemy.Height);
//检查是否碰撞
if (IntersectPixels(tankRectangle, tankTextureData, enemyRectangle, enemyTextureData))
{
personHit = true;
CreatExplosion(enemys[i]);
enemys[i] = new Vector2(0, 0);
//enemys.RemoveAt(i);
//i++;
}
//如果敌车超出范围,则减少
if (enemys[i].Y > Window.ClientBounds.Height)
{
enemys.RemoveAt(i);
i--;
}
}
}
private void CreatExplosion(Vector2 enemyPosition) //创建爆炸的位置
{
foreach (var ex in Explosions)
{
if (ex.alive == false)
{
ex.alive = true;
ex.currentExplosionX = 0;
ex.currentExplosionY = 0;
ex.position = new Vector2(enemyPosition.X - (explosionWith - enemy.Width) / 2, enemyPosition.Y-(explosionHeight-enemy.Height)/2);
break;
}
}
}
private void UpdateTankBullet() //更新炮弹的位置
{
foreach (var ball in tankBullet)
{
if (ball.alive == true)
{
ball.position -= ball.velocity; //如果炮弹存在则改变当前的位置
if (!rect.Contains(new Point((int)ball.position.X, (int)ball.position.Y)))
{
ball.alive = false; //如果炮弹飞出屏幕,判断失效
}
else
{
Rectangle rectBall = new Rectangle((int)ball.position.X, (int)ball.position.Y, ball.sprite.Width, ball.sprite.Height);
for (int i = 0; i < enemys.Count; i++)
{
Rectangle enemyRectangle = new Rectangle((int)enemys[i].X, (int)enemys[i].Y, enemy.Width, enemy.Height);
if (rectBall.Intersects(enemyRectangle))
{
Vector2 v = new Vector2((int)enemys[i].X, (int)enemys[i].Y);
CreatExplosion(v);
ball.alive = false;
enemys[i] = new Vector2(0, 0); //使敌车消失
}
}
}
}
else
{
}
}
}
上述代码就是具体控制tank及炮弹飞行,与敌车相碰及爆炸效果应该在哪点发生。付有注释通俗易懂,玩家请自己研究。
在玩家仔细看了源代码后,会有这样的疑问,为什么敌车生成与炮弹,爆炸效果的生成不一样,炮弹与爆炸效果都是基于
GameObject这个类,而敌车的出现却是List<Vector2> enemys = new List<Vector2>(); 对了,这是我故意区分的,
其实敌车也可以基于GameObject这个类,如果炮弹击中敌车,敌车也方便在屏幕上消失。那么为什么我非要这样写呢?
这样写也能达到GameObject的效果,同时在LoadContent中你会发现它并没有用for来循环加载,而是在Update循环中时时
更新数组中敌车的坐标,而被击中时我将敌车的位置放到(0,0)处,其实这样并不好,因为它并没有完全消失,这一点上就没有基于
GameObject类的写法好,如果用enemys.RemoveAt(i);程序就会报错,不信你试试!
如果玩家您有什么好的想法及建议,麻烦留言,大家一起研究!