XNA开发思考之3开动吧坦克

 此为原创,转载请注明作者和出处,谢谢!

先上连接,看实例演示http://v.youku.com/v_show/id_XMTAwMTYyMzEy.html

 坦克的旋转方向就不详细解释了,参看源码就能理解,其实也就是个了个弧度,利用弧度增加或减少控制其方向。这里主要是讲解,为什么坦克移动到某边时,视窗也跟着坦克移动。

const int ScreenWidth = 1024;  //定义的屏幕的宽
const int ScreenHeight = 768;  //定义屏幕的高
Vector2 tankPosition;  //tank的位置
Vector2 tankVelocity;  //tank移动的速度
Vector2 cameraPosition; //摄象机的位置

这里定义的tankPosition和tankVelocity是为了模仿真实的坦克移动和缓慢挺下的来的状态。

 1private void Updatetank()
 2{
 3     const float speedOftank = 0.75f//tanke的速度
 4      const float tankFriction = 0.9f//tanke移动的摩擦力
 5//当前键盘输入响应
 6      if (currenKeyboardState.IsKeyDown(Keys.Left))
 7                tank_rotation -= 0.1f;
 8     if (currenKeyboardState.IsKeyDown(Keys.Right))
 9                tank_rotation += 0.1f;
10     if (currenKeyboardState.IsKeyDown(Keys.Up))
11     {
12                tankVelocity.X -= speedOftank * ((float)Math.Cos(tank_rotation));
13                tankVelocity.Y -= speedOftank * ((float)Math.Sin(tank_rotation)); 
14     }

15     if (currenKeyboardState.IsKeyDown(Keys.Down))
16     {
17                tankVelocity.X += speedOftank * ((float)Math.Cos(tank_rotation));
18                tankVelocity.Y += speedOftank * ((float)Math.Sin(tank_rotation)); 
19     }

20//当前tanke的位置和速率的减小
21            tankPosition += tankVelocity;
22          tankVelocity *= tankFriction;   
23}
 

当tanke移到将近区域边缘时,则视窗(也叫摄象机位置)跟随tanke移动

 1 private void UpdateCamera()   //更新摄像机的位置
 2 {
 
3     Vector2 maxScroll = new Vector2(ScreenWidth,ScreenHeight)/2;  //获得屏幕的中点
 4      const float catSafeArea = 0.7f;   //tanke可移动的区域
 5      maxScroll *= catSafeArea;//防止tanke接触边框,计算移动区域大小
 6      //tanke移动最大区域的坐标(减少了坦克中心点到坦克边缘的距离)
 7     maxScroll -= new Vector2(tankTexture.Width, tankTexture.Height) / 2;
 
8     //tanke的最大最小的移动区域
 9      Vector2 min = tankPosition - maxScroll;
10     Vector2 max = tankPosition + maxScroll;
11      //限制摄像机的范围(与tanke可移动范围一致)
12      cameraPosition.X = MathHelper.Clamp(cameraPosition.X,min.X,max.X);
13      cameraPosition.Y = MathHelper.Clamp(cameraPosition.Y,min.Y,max.Y);
14 }

 绘制背景和tanke时单独写一个方法

 1 private void DrawBackground(Vector2 scrollOfset)  //绘制背景
 2 
 3             //摄像机与屏幕中点距离与背景图片的长宽比
 4    int tileX=(int)scrollOfset.X%backgroundTexture.Width;
 5    int tileY=(int)scrollOfset.Y%backgroundTexture.Height;
 6    if (tileX > 0)
 7         tileX -= backgroundTexture.Width;
 8    if (tileY > 0)
 9         tileY -= backgroundTexture.Height;
10    for(int x=tileX;x<ScreenWidth;x+=backgroundTexture.Width)
11     {
12         for (int y = tileY; y < ScreenHeight; y += backgroundTexture.Height)
13          {
14               spriteBatch.Draw(backgroundTexture,new Vector2(x,y),Color.White);
15           }
16     }
17 }
18 

在Draw()方法中添加如下代码:

Vector2 screenCenter = new Vector2(ScreenWidth,ScreenHeight)/2;
Vector2 scrollOfset 
= screenCenter - cameraPosition;

先获得屏幕中心点的坐标,然后利用中心点减去当前摄象机的位置,然后把值传入DrawBackground(scrollOfset);中

思考:我曾经另外用了一张背景图片,我发现初始状态绘制了4副背景图片。第一副图片的右下角,第2副图片的左下角,第3副图片的右上角,第4副图片的左上角。换句话说就是非传统的那样把一张背景图片平铺在荧幕上,而是4副背景图片的4个角拼接成。%是取余,为什么要用这样的算法,在就是光判断左上角贴片,如果向右移动或者向下移动,为什么不计算右边和下面的贴片呢?

 回答:下面就上述算法做下说明:(风海迷沙老师提供)

 

代码中就是像瓷砖一样的把地图无限拼接移动让游戏者感觉是在一个无尽的地图上移动。蓝色代表游戏窗口,红色块代表四个拼在一起的背影图片,那他们四个相交的地方就是你看到的效果,红色的左上角的坐标就是计算出来的(tileX,tileY),它们会始终为负,当他们为正时,就说明左上角已经移动进了屏幕区域,所以代码中如果tileX>0或tileY>0就会让其再向左或向上再补一个背景的宽度的距离。右边和下面也判断了,都在下面的嵌套if代码里,当坐标大于屏幕宽度就不再绘制背景了,也就是说左上角在屏幕内的都会被画出来。如果要算左上角的位置的话,取余数是最合适的啊,不然你怎么计算初始绘制位置?你也可以直接使用scrollOfset来作左上角的坐标,但是你无法预料每次update玩家会移动多少单位的背影区域,在某些情况下会在屏幕外绘制大量无效的绘制操作而浪费资源。

原文地址:https://www.cnblogs.com/315358525/p/1507934.html