Unity百游修炼(21)——C#和Unity实现坦克大战全网最详细(结尾有完整项目流程及源项目素材)

一·前言

        在游戏的浩瀚星空中,《坦克大战》无疑是一颗璀璨且经典的恒星。自问世以来,它凭借紧张刺激的对战玩法、丰富多变的关卡设计,深深烙印在无数玩家的记忆深处,成为了电子游戏发展历程中的一座里程碑。对于广大游戏开发爱好者而言,复刻这样一款经典游戏,不仅是一次向经典致敬的情怀之旅,更是一个全方位提升自身技能、深入探索游戏开发奥秘的绝佳契机。

二·视频效果展示

坦克大战

三·源项目以及素材

通过网盘分享的文件:坦克大战
链接: https://pan.baidu.com/s/1_WgxiXTH-WjKVT3pQwrJwA?pwd=1209 提取码: 1209 
--来自百度网盘超级会员v5的分享

四·详细制作步骤

1.工程创建和素材导入

(1)打开Unity,创建2D工程

(2)导入给予的素材

1

2.场景搭建和使用素材

(1)调整MainCamera距离和背景颜色

(2)修改界面比例大小为5:4

(3)检查资源:是否切割,Texture Type一定要是2D精灵单张还是多张取决于是否是单张图片还是图集

(4)图中资源为图集(且已经切割好,可以直接用了——点一个资源会展开很多图片

(5)如果发现有的图片没有进行切割,可以按照下面图示进行切割

 (6)创建项目游戏场景Scene,并且命名为 TankBattleScene

3.制作预制体

(1)把Player1第一张图片Hierarchy窗口,并修改名字为Player,并且调整大小如下图所示:

(2)创建一个文件夹名为Prefabs,用来存放预制体,并把Player拖入其中

(3)同理:把Map图集的每个图片按照上方的操作也是创建预制体并方法Prefabs文件夹中

这里补充一下:两个河流图片是后面用于做动画的,这里不需要拖入

(4)创建两个文件夹AnimationAnimationController

(5)按住Shift,选中Born图集的全部图集,一起拖放到Hierarchy窗口下

(6)调整动画大小,然后点击播放按钮显示

(7)修改名字,然后分别把产生的动画以及动画控制器放入相应的文件夹

(8)把Born也拖入Prefabs文件夹中,然后在Prefabs中创建子文件夹对其进行分类,如图:

(9)同理,创建Explode,Shield,River等效果,步骤同上

(补充一下:这里需要把河流效果也给加到这哦)

4.实现玩家移动

(1)创建一个文件夹Scripts用来存放脚本,在该文件下创建一个脚本,然后把脚本挂载到Player游戏物体上

(2)双击打开脚本,编辑脚本内容如下:(已经写好了代码的详细注释)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 定义一个名为 Player 的公共类,继承自 MonoBehaviour 类,这是 Unity 中所有脚本的基类
public class Player : MonoBehaviour
{
    // 定义一个公共的浮点型变量 speed,用于控制玩家的移动速度,初始值设为 3
    public float speed = 3;

    // Start 方法在脚本实例被启用时调用,通常用于初始化操作
    // 该方法在脚本生命周期中只会被调用一次,且在第一个 Update 方法调用之前执行
    void Start()
    {
        // 这里目前没有具体的初始化操作,可以根据需求添加相关代码
    }

    // Update 方法在每一帧都会被调用,用于处理游戏对象的逻辑更新
    void Update()
    {
        // 获取水平方向的输入值,使用 Input.GetAxisRaw 方法,它返回 -1、0 或 1
        // "Horizontal" 是 Unity 中预定义的输入轴名称,通常对应键盘上的左右箭头键或 A、D 键
        float x = Input.GetAxisRaw("Horizontal");

        // 使用 transform.Translate 方法移动游戏对象
        // Vector3.right 表示世界坐标系中的右方向,即 X 轴正方向
        // x * speed * Time.deltaTime 计算出在当前帧中应该移动的距离
        // Space.World 表示使用世界坐标系进行移动
        transform.Translate(Vector3.right * x * speed * Time.deltaTime, Space.World);

        // 获取垂直方向的输入值,使用 Input.GetAxisRaw 方法,它返回 -1、0 或 1
        // "Vertical" 是 Unity 中预定义的输入轴名称,通常对应键盘上的上下箭头键或 W、S 键
        float y = Input.GetAxisRaw("Vertical");

        // 使用 transform.Translate 方法移动游戏对象
        // Vector3.up 表示世界坐标系中的上方向,即 Y 轴正方向
        // y * speed * Time.deltaTime 计算出在当前帧中应该移动的距离
        // Space.World 表示使用世界坐标系进行移动
        transform.Translate(Vector3.up * y * speed * Time.deltaTime, Space.World);
    }
}

(3)保存代码,启动unity项目,使用wasd键即可移动(方向键也可以)

5.控制坦克移动朝向进行切换

(1)打开Player脚本进行编辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float speed = 3;
    // 定义一个 SpriteRenderer 类型的私有变量 picture,用于获取和控制该游戏对象上的 SpriteRenderer 组件
    private SpriteRenderer picture;
    // 定义一个公共的 Sprite 数组 TankDirection,用于存储坦克不同方向的精灵图片,按照上、右、下、左的顺序排列
    public Sprite[] TankDirection;//上右下左
    private void Awake()
    {
        // 在 Awake 方法中获取该游戏对象上的 SpriteRenderer 组件,并赋值给 picture 变量
        picture = GetComponent<SpriteRenderer>();
    } 

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        float x = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * x*speed*Time.deltaTime,Space.World);
        // 判断水平输入值 x 是否小于 0,若小于 0 表示玩家按下了向左的按键
        if(x<0)
        {
            // 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 3 的精灵图片,即坦克向左的图片
            picture.sprite = TankDirection[3];//左边
        }
        // 判断水平输入值 x 是否大于 0,若大于 0 表示玩家按下了向右的按键
        else if (x > 0)
        {
            // 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 1 的精灵图片,即坦克向右的图片
            picture.sprite = TankDirection[1];//右边
        }
       

        float y = Input.GetAxisRaw("Vertical");
        transform.Translate(Vector3.up * y * speed * Time.deltaTime, Space.World);
        // 判断垂直输入值 y 是否小于 0,若小于 0 表示玩家按下了向下的按键
        if (y < 0)
        {
            // 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 2 的精灵图片,即坦克向下的图片
            picture.sprite = TankDirection[2];//下边
        }
        // 判断垂直输入值 y 是否大于 0,若大于 0 表示玩家按下了向上的按键
        else if (y > 0)
        {
            // 将 SpriteRenderer 组件的 sprite 属性设置为 TankDirection 数组中索引为 0 的精灵图片,即坦克向上的图片
            picture.sprite = TankDirection[0];//上边
        }
    }
}

(2)选中脚本挂载的对象Player,把图片朝向的图片分别拖入到如图位置

6.碰撞器的添加(解决抖动问题)

(1)给prefabs文件中预制体Player添加Box Collider 2D碰撞器组件和Rigidbody组件,同时调节组件参数如下:

(2)给Map地图上作为障碍过不去的预制体添加Box Collider 2D碰撞器组件即可,如下:

(3)修复Player碰撞偏移Bug

(4)解决坦克碰撞鬼畜现象(修改代码)

总结:把Update的代码全部放入Fixedupdate里面,同时time属性也改一下,如下图

7.移动优先级的添加(2D渲染层级的问题)

(1)修改移动的优先级(避免同时按下两个键就会导致斜着移动)增添圈出来的代码即可

(2)修改代码  整理代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float speed = 3;
    private SpriteRenderer picture;
    public Sprite[] TankDirection;//上右下左


    private void Awake()
    {
        picture = GetComponent<SpriteRenderer>();
    } 

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void FixedUpdate()
    {
        Move();
    }

    public void Move()
    {
        float x = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
        if (x < 0)
        {
            picture.sprite = TankDirection[3];//左边
        }
        else if (x > 0)
        {
            picture.sprite = TankDirection[1];//右边
        }

        if (x != 0)
        {
            return;//保证同时按着水平和垂直方向上  水平优先
        }


        float y = Input.GetAxisRaw("Vertical");
        transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
        if (y < 0)
        {
            picture.sprite = TankDirection[2];//下边
        }
        else if (y > 0)
        {
            picture.sprite = TankDirection[0];//上边
        }
    }
}

(3)设置优先级:特效>tank

数值越大,越后渲染,覆盖最上面

Born 和 Shield  和  Bullet 等渲染数值为1

例如;我们需要Tank可以从 Grass下面窜过去

坦克大战穿越草坪

(4)制作子弹,拖入bullet的一张照片,然后大小为 3  3  1,命名为Bullet,制作预制体

8.坦克的攻击方法

(1)打开Player脚本,写一个攻击方法 Attack()

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float speed = 3;
    private SpriteRenderer picture;
    public Sprite[] TankDirection;//上右下左
    public GameObject bulletPre;

    private void Awake()
    {
        picture = GetComponent<SpriteRenderer>();
    } 

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
         Attack();
    }

    private void FixedUpdate()
    {
        Move();
    }

    private void Attack()
    {
        if ((Input.GetKeyDown(KeyCode.Space)))
        {
            Instantiate(bulletPre, transform.position, transform.rotation);
        }
    }

    public void Move()
    {
        float x = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
        if (x < 0)
        {
            picture.sprite = TankDirection[3];//左边
        }
        else if (x > 0)
        {
            picture.sprite = TankDirection[1];//右边
        }

        if (x != 0)
        {
            return;//保证同时按着水平和垂直方向上  水平优先
        }


        float y = Input.GetAxisRaw("Vertical");
        transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
        if (y < 0)
        {
            picture.sprite = TankDirection[2];//下边
        }
        else if (y > 0)
        {
            picture.sprite = TankDirection[0];//上边
        }
    }
}

(2)把子弹预制体拖入到脚本上

(3)测试

空格键可以生成子弹

9.子弹朝向

(1)实例化子弹函数修改生成角度

(2)代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float speed = 3;
    private Vector3 bulletEulerAngles;
    private SpriteRenderer picture;
    public Sprite[] TankDirection;//上右下左
    public GameObject bulletPre;

    private void Awake()
    {
        picture = GetComponent<SpriteRenderer>();
    } 

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Attack();
    }

    private void FixedUpdate()
    {
        Move();
    }

    private void Attack()
    {
        if ((Input.GetKeyDown(KeyCode.Space)))
        {
            //子弹产生的角度:当前坦克角度+子弹应该旋转的角度
            Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles+bulletEulerAngles));
        }
    }

    public void Move()
    {
        float x = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
        if (x < 0)
        {
            picture.sprite = TankDirection[3];//左边
            bulletEulerAngles = new Vector3(0, 0, 90);
        }
        else if (x > 0)
        {
            picture.sprite = TankDirection[1];//右边
            bulletEulerAngles = new Vector3(0, 0, -90);
        }

        if (x != 0)
        {
            return;//保证同时按着水平和垂直方向上  水平优先
        }


        float y = Input.GetAxisRaw("Vertical");
        transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
        if (y < 0)
        {
            picture.sprite = TankDirection[2];//下边
            bulletEulerAngles = new Vector3(0, 0, -180);
        }
        else if (y > 0)
        {
            picture.sprite = TankDirection[0];//上边
            bulletEulerAngles = new Vector3(0, 0, 0);
        }
    }
}

10.让子弹动起来,攻击CD

(1)创建子弹脚本Bullet,并挂载到子弹预制体上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float movespeed = 5;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(transform.up * movespeed * Time.deltaTime, Space.World);
    }
}

(2)增加攻击间隔

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    private float timeval;
    public float speed = 3;
    private Vector3 bulletEulerAngles;
    private SpriteRenderer picture;
    public Sprite[] TankDirection;//上右下左
    public GameObject bulletPre;

    private void Awake()
    {
        picture = GetComponent<SpriteRenderer>();
    } 

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
       if(timeval>=0.4f)
        {
            Attack();
        }
       else
        {
            timeval += Time.deltaTime;
        }
    }

    private void FixedUpdate()
    {
        Move();
    }

    private void Attack()
    {
        if ((Input.GetKeyDown(KeyCode.Space)))
        {
            //子弹产生的角度:当前坦克角度+子弹应该旋转的角度
            Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles+bulletEulerAngles));
            timeval = 0;
        }
    }

    public void Move()
    {
        float x = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
        if (x < 0)
        {
            picture.sprite = TankDirection[3];//左边
            bulletEulerAngles = new Vector3(0, 0, 90);
        }
        else if (x > 0)
        {
            picture.sprite = TankDirection[1];//右边
            bulletEulerAngles = new Vector3(0, 0, -90);
        }

        if (x != 0)
        {
            return;//保证同时按着水平和垂直方向上  水平优先
        }


        float y = Input.GetAxisRaw("Vertical");
        transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
        if (y < 0)
        {
            picture.sprite = TankDirection[2];//下边
            bulletEulerAngles = new Vector3(0, 0, -180);
        }
        else if (y > 0)
        {
            picture.sprite = TankDirection[0];//上边
            bulletEulerAngles = new Vector3(0, 0, 0);
        }
    }
}

 (3)给子弹Bullet添加Box Collider2D和rigidbody ,然后选择 Is trigger,取消重力

11.空气墙的制作--添加标签区分

(1)新建标签tag

(2)制作空气墙AirWall

12.玩家无敌方法与死亡方法的编写

(1)在player脚本中创建玩家死亡方法

    private void Die()
    {
        if(isDefend)
        {
            return;
        }
        //产生爆炸特效
        Instantiate(explosionpre,transform.position,transform.rotation);
        //死亡
        Destroy(gameObject);
    }

(2)创建Explosion脚本,然后挂载到爆炸效果上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Explosion : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Destroy(gameObject,0.167f);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

(3)打开bullet脚本

    private void OnTriggerEnter2D(Collider2D collision)
    {
        switch(collision.tag)
        {
            case "Tank":
                collision.SendMessage("Die");
                break;
            case "Wall":
                break;
            case "Enemy":
                break;
            case "Iron":
                break;
            case "Heart":
                break;
        }
    }

(4)挂载脚本需要的物体

(5)把保护盾挂载到player下面作为子物体(根据需要启动或者关闭保护,减少性能)

 (6)开局限时保护

 if (isDefend)// 保护状态
 {
     defeneffectpre.SetActive(true);
     defendtimeval-=Time.deltaTime;
     if (defendtimeval <=0 )
     {
          defeneffectpre.SetActive(false);
         isDefend = false;
     }

13.区别玩家的子弹与敌人的子弹

(1)打开bullet脚本完善代码

    private void OnTriggerEnter2D(Collider2D collision)
    {
        switch(collision.tag)
        {
            case "Tank":
                if(!isPlayerBullet)
                {
                    collision.SendMessage("Die");
                }
                break;
            case "Wall":
                Destroy(collision.gameObject);
                Destroy(gameObject);
                break;
            case "Enemy":
                collision.SendMessage("Die");
                Destroy(gameObject);
                break;
            case "Iron":
                Destroy(gameObject);
                break;
            case "Heart":
                collision.SendMessage("Die");
                break;

        }
    }

(2)创建一个Heart脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Heart : MonoBehaviour
{
    private SpriteRenderer sr;
    public Sprite pic;
    // Start is called before the first frame update
    void Start()
    {
        sr = GetComponent<SpriteRenderer>();
    }
     
    // Update is called once per frame
    void Update()
    {
        
    }

    public void Die()
    {
        sr.sprite = pic;
    }
}

14.制作敌人的子弹

(1)复制子弹预制体,修改名字,并且取消 是玩家子弹这一项

15.制作敌人,添加玩家产生效果

(1)复制Player预制体,修改为Enemy,删除Player脚本,修改渲染图片

(2)添加一个Enemy脚本,先复制Player脚本的内容,暂且不做处理

(3)增加一个Heart死亡后,爆炸效果,并且挂载到Heart上面

    public void Die()
    {
        sr.sprite = pic;
        Instantiate(Explodeeffctpre, transform.position, transform.rotation);
    }

(4)增加一个Born出生特效

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Born : MonoBehaviour
{
    public GameObject playerpre;
    // Start is called before the first frame update
    void Start()
    {
        Invoke("BornTank", 0.8f);
        Destroy(gameObject,0.8f);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void BornTank()
    {
        Instantiate(playerpre,transform.position,Quaternion.identity);
    }
}

16.使用15的特效产生敌人

(1)创建两个敌人(图片不同的两种敌人),挂载Enemy脚本,然后给脚本拖拽需要的资源图片

(2)添加资源和修改参数

(3)打开Born脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Born : MonoBehaviour
{
    public GameObject playerpre;
    public GameObject[] Enemy;

    public bool CreatPlayer;
    // Start is called before the first frame update
    void Start()
    {
        Invoke("BornTank", 0.8f);
        Destroy(gameObject,0.8f);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void BornTank()
    {
        if(CreatPlayer)
        {
            Instantiate(playerpre, transform.position, Quaternion.identity);
        }
        else
        {
            int num=Random.Range(0, 2);
            Instantiate(Enemy[num], transform.position, Quaternion.identity);
        }
        
    }
}

(4)添加脚本需要的资源,然后创建多个Bron物体,运行结果

17.编写敌人ai

(1)删除Enemy脚本的所有保护效果代码

(2)添加自动发射子弹代码

    void Update()
    {
        if (timeval >= 3f)
        {
            Attack();
        }
        else
        {
            timeval += Time.deltaTime;
        }
    }

    private void Attack()
    {
            Instantiate(bulletPre, transform.position, Quaternion.Euler(transform.eulerAngles + bulletEulerAngles));
            timeval = 0;
    }

(3)添加自动移动代码

 public void Move()
 {
     if(changedirctiontime>=4)
     {
         int num=Random.Range(0, 8);
         if (num > 5)
         {
             y = -1; x = 0;
         }
         else if (num == 0)
         {
             y = 1; x = 0;
         }
         else if (num > 0 && num <= 2)
         {
             y = 0; x = -1;
         }
         else if(num>2&&num<=4)
         {
             y = 0; x = 1;
         } 
         changedirctiontime = 0;
     }
     else
     {
         changedirctiontime += Time.fixedDeltaTime;
     }
     transform.Translate(Vector3.right * x * speed * Time.fixedDeltaTime, Space.World);
     if (x < 0)
     {
         picture.sprite = TankDirection[3];//左边
         bulletEulerAngles = new Vector3(0, 0, 90);
     }
     else if (x > 0)
     {
         picture.sprite = TankDirection[1];//右边
         bulletEulerAngles = new Vector3(0, 0, -90);
     }
     transform.Translate(Vector3.up * y * speed * Time.fixedDeltaTime, Space.World);
     if (y < 0)
     {
         picture.sprite = TankDirection[2];//下边
         bulletEulerAngles = new Vector3(0, 0, -180);
     }
     else if (y > 0)
     {
         picture.sprite = TankDirection[0];//上边
         bulletEulerAngles = new Vector3(0, 0, 0);
     }
 }

18.初始化地图工具的创建

(1)创建一个空物体MapCreate,创建一个Map脚本,挂载到MapCreate上,创建基地和周围的围墙

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Map : MonoBehaviour
{    
    public GameObject[] item;//初始化地图物体数组

    private void Awake()
    {
        //基地以及周围围墙
        CreateItem(item[0],new Vector3(0,-8,0),Quaternion.identity);//实例老家
        CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(1, -7, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(-1, -7, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(0, -7, 0), Quaternion.identity);//实例wall



    }


    private void CreateItem(GameObject creatgameobject,Vector3 creatposition,Quaternion ro)
    {
        GameObject itemGo=Instantiate(creatgameobject,creatposition,ro);
        itemGo.transform.SetParent(gameObject.transform);
    }
}

(2)实例化地图

 //实例化地图
 for (int i = 0; i < 60; i++)
 {
     CreateItem(item[1], CreatRandomPosition(), Quaternion.identity);
 }
 for (int i = 0; i < 20; i++)
 {
     CreateItem(item[2], CreatRandomPosition(), Quaternion.identity);
 }
 for (int i = 0; i < 20; i++)
 {
     CreateItem(item[4], CreatRandomPosition(), Quaternion.identity);
 }
 for (int i = 0; i < 20; i++)
 {
     CreateItem(item[5], CreatRandomPosition(), Quaternion.identity);
 }

19.编写产生随机位置的方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Map : MonoBehaviour
{
    public GameObject[] item;//初始化地图物体数组

    private List<Vector3> itempositonlist = new List<Vector3>();//已经有东西的位置列表

    private void Awake()
    {
        //基地以及周围围墙
        CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity);//实例老家
        CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(1, -7, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(-1, -7, 0), Quaternion.identity);//实例wall
        CreateItem(item[1], new Vector3(0, -7, 0), Quaternion.identity);//实例wall


        //实例化外围墙
        for (int i = -11; i < 12; i++)
        {
            CreateItem(item[6], new Vector3(i, -9, 0), Quaternion.identity);
        }
        for (int i = -11; i < 12; i++)
        {
            CreateItem(item[6], new Vector3(i, 9, 0), Quaternion.identity);
        }
        for (int i = -8; i < 9; i++)
        {
            CreateItem(item[6], new Vector3(-11, i, 0), Quaternion.identity);
        }
        for (int i = -8; i < 9; i++)
        {
            CreateItem(item[6], new Vector3(11, i, 0), Quaternion.identity);
        }


        //实例化地图
        for (int i = 0; i < 60; i++)
        {
            CreateItem(item[1], CreatRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[2], CreatRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[4], CreatRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[5], CreatRandomPosition(), Quaternion.identity);
        }


        // 初始化玩家
        GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity);
        go.GetComponent<Born>().CreatPlayer = true;

        //初始化敌人;
        CreateItem(item[3], new Vector3(-10, 8, 0), Quaternion.identity);
        CreateItem(item[3], new Vector3(0, 8, 0), Quaternion.identity);
        CreateItem(item[3], new Vector3(10, 8, 0), Quaternion.identity);

        InvokeRepeating("CreateEnemy",4,5);


    }


    private void CreateItem(GameObject creatgameobject, Vector3 creatposition, Quaternion ro)
    {
        GameObject itemGo = Instantiate(creatgameobject, creatposition, ro);
        itemGo.transform.SetParent(gameObject.transform);
        itempositonlist.Add(creatposition);
    }

    private Vector3 CreatRandomPosition()
    {
        while (true)
        {
            Vector3 createposiyion = new Vector3(Random.Range(-9, 10), Random.Range(-7, 8), 0);
            if (!HasthePosition(createposiyion))
            {
                return createposiyion;
            }

        }
    }

    private bool HasthePosition(Vector3 createpos)
    {
        for (int i = 0; i < itempositonlist.Count; i++)
        {
            if (createpos == itempositonlist[i])
            {
                return true;
            }
        }
        return false;
    }

    private void CreateEnemy()
    {
        int num = Random.Range(0, 3);
        Vector3 EnemyPos=new Vector3();
        if(num==0)
        {
            EnemyPos = new Vector3(-10,8,0);
        }
        else if(num==1)
        {
            EnemyPos=new Vector3(0,8,0);
        }
        else
        {
            EnemyPos=new Vector3(10,8,0);
        }
        CreateItem(item[3], EnemyPos, Quaternion.identity);
    }
}

20.添加音效

(1)添加开头音乐(创建一个空物体,挂载该组件,然后挂载音乐)

(2)添加发射子弹音效


五·结语

        在这个过程中,我们不仅掌握了 Unity 引擎诸多核心功能的运用,更对游戏开发流程有了系统性的认知。希望这份指南能为你的游戏开发之旅点亮一盏灯,激励你在此基础上继续拓展创新,为玩家带来更精彩、更具沉浸感的坦克大战体验。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值