fc坦克大战游戏完美复刻


一、 介绍

儿时经典游戏《坦克大战》完整复刻
发射子弹、生成敌人、消灭敌人、保护老巢、记录生命值。

在这里插入图片描述

在这里插入图片描述


二、 制作基本物体

制作玩家坦克
制作敌人坦克
制作玩家心脏
制作玩家保护罩
制作河流
制作砖头墙
制作铁墙
制作出生位置
制作出生动画

在这里插入图片描述


三、 控制玩家坦克移动、转向

wsad移动
数组控制转向
定义子弹发射时间间隔
发射子弹
坦克死亡效果

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

public class Player : MonoBehaviour {

  //定义坦克的移动速度属性
public float moveSpeed=3;

//定义子弹的旋转角度
private Vector3 bullectEulerAngles;

//定义攻击CD时间
private float timeVal;

//定义无敌状态持续时间
private float defendTimeVal=3;

//定义是否处于无敌状态
private bool isDefended=true;

//引用组件
private SpriteRenderer sr; //获取坦克的SpriteRenderer组件
public Sprite[] tankSprite; //存储坦克的四个方向的图片
public GameObject bullectPrefab; //存储子弹的预制体
public GameObject explosionPrefab; //存储爆炸特效的预制体
public GameObject defendEffectPrefab; //存储无敌特效的预制体
public AudioSource moveAudio; //获取坦克的AudioSource组件
public AudioClip[] tankAudio; //存储坦克的音效

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

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        //是否处于无敌状态
        if (isDefended)
        {
            defendEffectPrefab.SetActive(true);
            defendTimeVal -= Time.deltaTime;
            if (defendTimeVal<=0)
            {
                isDefended = false;
                defendEffectPrefab.SetActive(false);
            }
        }

        
       
    }

    private void FixedUpdate()
    {
        if (PlayerManager.Instance.isDefeat)
        {
            return;
        }
        Move();
        //攻击的CD
        if (timeVal >= 0.4f)
        {
            Attack();
        }
        else
        {
            timeVal += Time.fixedDeltaTime;
        }


    }

    //坦克的攻击方法
    private void Attack()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {

            //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度。
            Instantiate(bullectPrefab, transform.position,Quaternion.Euler(transform.eulerAngles+bullectEulerAngles));
            timeVal = 0;
        }
    }


    //坦克的移动方法
    private void Move()
    {
        float v = Input.GetAxisRaw("Vertical");


        transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);

        if (v < 0)
        {
            sr.sprite = tankSprite[2];
            bullectEulerAngles = new Vector3(0, 0, -180);
        }

        else if (v > 0)
        {
            sr.sprite = tankSprite[0];
            bullectEulerAngles = new Vector3(0, 0, 0);
        }

        if (Mathf.Abs(v)>0.05f)
        {
            moveAudio.clip = tankAudio[1];
            
            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }

        if (v != 0)
        {
            return;
        }

        float h = Input.GetAxisRaw("Horizontal");
        transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);
        if (h < 0)
        {
            sr.sprite = tankSprite[3];
            bullectEulerAngles = new Vector3(0, 0, 90);
        }

        else if (h > 0)
        {
            sr.sprite = tankSprite[1];
            bullectEulerAngles = new Vector3(0, 0, -90);
        }

        if (Mathf.Abs(h) > 0.05f)
        {
            moveAudio.clip = tankAudio[1];

            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }
        else
        {
            moveAudio.clip = tankAudio[0];

            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }
    }

    //坦克的死亡方法
    private void Die()
    {
        if (isDefended)
        {
            return;
        }

        PlayerManager.Instance.isDead = true;

        //产生爆炸特效
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        //死亡
        Destroy(gameObject);
    }	
}





四、 子弹脚本、爆炸脚本

子弹撞击不同的物体,会有不同的效果
播放爆炸动画效果
设置子弹移动速度
检测是否为玩家子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullect : MonoBehaviour {

    public float moveSpeed = 10;

    public bool isPlayerBullect;


	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        transform.Translate(transform.up * moveSpeed * Time.deltaTime, Space.World);
	}


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

}

爆炸效果
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Explosion : MonoBehaviour {

	// Use this for initialization
	void Start () {
        Destroy(gameObject, 0.167f);
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}


五、 敌人AI寻路算法

敌人在上面有三个刷新点
出生后每三秒发射一枚子弹
敌人走动的方向,是random随机值,往下走的概率最大
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour {

    //属性值
    public float moveSpeed = 3;
    private Vector3 bullectEulerAngles; // 子弹的旋转角度
    private float v = -1; // 坦克的竖直方向的移动量
    private float h; // 坦克的水平方向的移动量

    //引用
    private SpriteRenderer sr; // 坦克的SpriteRenderer组件
    public Sprite[] tankSprite; // 坦克朝向四个方向的精灵
    public GameObject bullectPrefab; // 子弹的预制体
    public GameObject explosionPrefab; // 爆炸特效的预制体

    //计时器
    private float timeVal; // 攻击计时器
    private float timeValChangeDirection; // 改变方向计时器

    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>(); // 获取坦克的SpriteRenderer组件
    }

    void Start()
    {

    }

    void Update()
    {
        //攻击的时间间隔
        if (timeVal >= 3) // 如果攻击计时器大于等于3秒
        {
            Attack(); // 调用坦克的攻击方法
        }
        else
        {
            timeVal += Time.deltaTime; // 否则,增加攻击计时器的值
        }

    }

    private void FixedUpdate()
    {
        Move(); // 每一帧调用坦克的移动方法
    }

    //坦克的攻击方法
    private void Attack()
    {
       //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度。
       Instantiate(bullectPrefab, transform.position, Quaternion.Euler(transform.eulerAngles + bullectEulerAngles)); // 实例化子弹预制体
       timeVal = 0; // 重置攻击计时器的值
    }

   //坦克的移动方法
private void Move()
{
    // 当计时器 timeValChangeDirection 大于等于4时,改变坦克的移动方向
    if (timeValChangeDirection >= 4)
    {
        // 随机一个数值
        int num = Random.Range(0, 8);
        if (num > 5)
        {
            v = -1;
            h = 0;
        }
        else if (num == 0)
        {
            v = 1;
            h = 0;
        }
        else if (num > 0 && num <= 2)
        {
            h = -1;
            v = 0;
        }
        else if (num > 2 && num <= 4)
        {
            h = 1;
            v = 0;
        }
        // 重置计时器
        timeValChangeDirection = 0;
    }
    else
    {
        // 计时器加上时间差
        timeValChangeDirection += Time.fixedDeltaTime;
    }

    // 沿着当前方向移动坦克
    transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);

    // 根据当前方向改变坦克的朝向
    if (v < 0)
    {
        sr.sprite = tankSprite[2];
        bullectEulerAngles = new Vector3(0, 0, -180);
    }
    else if (v > 0)
    {
        sr.sprite = tankSprite[0];
        bullectEulerAngles = new Vector3(0, 0, 0);
    }

    // 如果坦克在竖直方向上,则不进行水平方向的移动
    if (v != 0)
    {
        return;
    }

    // 沿着当前方向移动坦克
    transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World);

    // 根据当前方向改变坦克的朝向
    if (h < 0)
    {
        sr.sprite = tankSprite[3];
        bullectEulerAngles = new Vector3(0, 0, 90);
    }
    else if (h > 0)
    {
        sr.sprite = tankSprite[1];
        bullectEulerAngles = new Vector3(0, 0, -90);
    }
}

//坦克的死亡方法
private void Die()
{
    // 玩家得分加1
    PlayerManager.Instance.playerScore++;
    // 产生爆炸特效
    Instantiate(explosionPrefab, transform.position, transform.rotation);
    // 销毁坦克
    Destroy(gameObject);
}

// 当坦克与其他游戏对象发生碰撞时,检测是否为敌方坦克,如果是,则重置移动方向
private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "Enemy")
    {
        timeValChangeDirection = 4;
    }
}

六、 坦克生成点脚本

上面三个出生点刷新敌人坦克,有两种坦克,随机产生一种
下面两个出生点刷新玩家坦克
延时调用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Born : MonoBehaviour {
    // 玩家坦克预制件
    public GameObject playerPrefab;
    // 敌人坦克预制件数组
    public GameObject[] enemyPrefabList;
    // 是否产生玩家坦克
    public bool createPlayer;

    // Use this for initialization
    void Start () {
        // 延迟1秒后执行 BornTank 方法
        Invoke("BornTank", 1f);
        // 一秒后销毁当前游戏对象
        Destroy(gameObject, 1);
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    // 产生坦克的方法
    private void BornTank()
    {
        if (createPlayer)
        {
            // 产生玩家坦克
            Instantiate(playerPrefab, transform.position, Quaternion.identity);
        }
        else
        {
            // 随机选择一种敌人坦克类型
            int num = Random.Range(0, 2);
            // 产生敌人坦克
            Instantiate(enemyPrefabList[num], transform.position, Quaternion.identity);
        }
    }
}

七、 用链表实例化地图

游戏地图并不是预先设置好的,是随机生成的(除了心脏),每次都不一样

在这里插入图片描述

在这里插入图片描述

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

public class MapCreation : MonoBehaviour {

    // 用来装饰初始化地图所需物体的数组。
    // 0.老家 1.墙 2.障碍 3.出生效果 4.河流 5.草 6.空气墙
    public GameObject[] item;

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

    private void Awake()
    {
        InitMap();
    }

    private void InitMap()
    {
        //实例化老家
        CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity);
        //用墙把老家围起来
        CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);
        CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);
        for (int i = -1; i < 2; i++)
        {
            CreateItem(item[1], new Vector3(i, -7, 0), Quaternion.identity);
        }
        //实例化外围墙
        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);
        }

        //初始化玩家
        GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity);
        go.GetComponent<Born>().createPlayer = 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);

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

    // 创建物体的方法
    private void CreateItem(GameObject createCameObject, Vector3 createPosition, Quaternion createRotation)
    {
        GameObject itemGo = Instantiate(createCameObject, createPosition, createRotation);
        itemGo.transform.SetParent(gameObject.transform);
        itemPositionList.Add(createPosition);
    }

  // 产生随机位置的方法
private Vector3 CreateRandomPosition()
{
    // 不生成x=-10,10的两列,y=-8,8正两行的位置
    while (true)
    {
        Vector3 createPosition = new Vector3(Random.Range(-9, 10), Random.Range(-7, 8), 0);
        
        // 判断是否该位置是否已经存在
        if (!HasThePosition(createPosition))
        {
            return createPosition;
        }
    }
}

//用来判断位置列表中是否有这个位置
private bool HasThePosition(Vector3 createPos)
{
    // itemPositionList 是之前记录所有敌人和道具生成位置的列表
    for (int i = 0; i < itemPositionList.Count; i++)
    {
        if (createPos == itemPositionList[i])
        {
            return true;
        }
    }
    
    return false;
}

//产生敌人的方法
private void CreateEnemy()
{
    int num = Random.Range(0, 3);
    Vector3 EnemyPos = new Vector3();
    
    // 随机选择一个 Y 轴最高(即在顶部)的位置作为敌人的位置
    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);
}

心脏周边的墙体不是随机的,每次都必须预先设定好

在这里插入图片描述

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

public class Heart : MonoBehaviour {
    // SpriteRenderer组件,用于设置坦克被击中后显示的图片
    private SpriteRenderer sr;
    // 爆炸特效预制件
    public GameObject explosionPrefab;
    // 死亡音效
    public AudioClip dieAudio;
    // 被击中后显示的破碎图片
    public Sprite BrokenSprite;

    // Use this for initialization
    void Start () {
        // 获取SpriteRenderer组件
        sr = GetComponent<SpriteRenderer>();
    }
    
    // ...

    // 坦克死亡的方法
    public void Die()
    {
        // 将坦克的SpriteRenderer组件的图片设置为破碎图片
        sr.sprite = BrokenSprite;
        // 产生爆炸特效
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        // 设置PlayerManager类中的isDefeat变量为true,表示游戏失败
        PlayerManager.Instance.isDefeat = true;
        // 在坦克死亡的位置播放死亡音效
        AudioSource.PlayClipAtPoint(dieAudio, transform.position);
    }
}

八、 玩家游戏控制器脚本

负责游戏的基本逻辑
设置玩家生命值
设置分数
using System.Collections; // 导入系统集合命名空间
using System.Collections.Generic; // 导入泛型集合命名空间
using UnityEngine; // 导入 Unity 引擎命名空间
using UnityEngine.UI; // 导入 Unity 引擎 UI 命名空间
using UnityEngine.SceneManagement; // 导入 Unity 引擎场景管理命名空间

public class PlayerManager : MonoBehaviour { // 声明一个名为 PlayerManager 的类并继承自 MonoBehaviour

    // 属性值
    public int lifeValue = 3; // 玩家生命值,默认为 3
    public int playerScore = 0; // 玩家分数,默认为 0
    public bool isDead; // 是否死亡,默认为 false
    public bool isDefeat; // 是否失败,默认为 false

    // 引用
    public GameObject born; // 出生点游戏对象
    public Text playerScoreText; // 显示玩家分数的文本对象
    public Text PlayerLifeValueText; // 显示玩家生命值的文本对象
    public GameObject isDefeatUI; // 显示失败界面的游戏对象

    // 单例
    private static PlayerManager instance; // 声明一个静态的 PlayerManager 对象

    public static PlayerManager Instance // 声明一个静态的 Instance 属性
    {
        get
        {
            return instance; // 返回 instance 对象
        }

        set
        {
            instance = value; // 设置 instance 对象
        }
    }

    private void Awake() // Awake 方法在脚本实例被加载时执行
    {
        Instance = this; // 设置 instance 对象为当前实例
    }

    // Use this for initialization
    void Start () { // Start 方法在脚本实例被启用时执行,本例中未实现
		
	}
	
	// Update is called once per frame
	void Update () { // Update 方法在每一帧渲染时执行
        if (isDefeat) // 如果失败
        {
            isDefeatUI.SetActive(true); // 显示失败界面
            Invoke("ReturnToTheMainMenu", 3); // 延迟 3 秒后返回主界面
            return; // 返回
        }
        if (isDead) // 如果死亡
        {
            Recover(); // 复活
        }
        playerScoreText.text = playerScore.ToString(); // 更新分数文本
        PlayerLifeValueText.text = lifeValue.ToString(); // 更新生命值文本
	}

    private void Recover() // 复活方法
    {
        if (lifeValue<=0) // 如果生命值小于等于 0
        {
            // 游戏失败,返回主界面
            isDefeat = true; // 设置失败状态为 true
            Invoke("ReturnToTheMainMenu", 3); // 延迟 3 秒后返回主界面
        }
        else // 如果生命值大于 0
        {
            lifeValue--; // 生命值减一
            GameObject go = Instantiate(born, new Vector3(-2, -8, 0), Quaternion.identity); // 在出生点位置实例化玩家
            go.GetComponent<Born>().createPlayer = true; // 设置出生点的 createPlayer 属性为 true
            isDead = false; // 设置死亡状态为 false
        }
    }

    private void ReturnToTheMainMenu() // 返回主界面方法
    {
        SceneManager.LoadScene(0); // 加载场景 0
    }
}

九、 添加音效

添加游戏音效、坦克行进音效、开火音效。
增加双人模式。
添加ui
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Option : MonoBehaviour {

    private int choice = 1;
    public Transform posOne;
    public Transform posTwo;

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        if (Input.GetKeyDown(KeyCode.W))
        {
            choice = 1;
            transform.position = posOne.position;
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            choice = 2;
            transform.position = posTwo.position;
        }
        if (choice==1&&Input.GetKeyDown(KeyCode.Space))
        {
            SceneManager.LoadScene(1);
        }
	}
}

在这里插入图片描述

十、 资源包

https://pan.baidu.com/s/1eGVzYU-pQLZntuGcFfm0Ww

密码:exqi

工程文件私






  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忽然602

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值