Unity3D实现坦克大战

一、效果图演示

Unity3D实现坦克大战_第1张图片

二、逻辑剖析

从界面上:

  • 需要一个Canvas满屏对着用户,该Canvas上展示用户的游戏数据,比如血条。
  • 需要一个Canvas放在蓝色坦克上方,也需要实时对着用户,显示敌人的血条信息
  • 两个坦克
  • 一个平面Plane放草地的纹理

从逻辑上:

  • 前后箭头键控制玩家前进或后退
  • 左右箭头键控制玩家左右转向
  • 鼠标左键或空格键控制玩家发射炮弹
  • 玩家血条希纳是在屏幕左上角
  • 相机在玩家后上方的位置,始终跟随玩家,朝玩家正前方看
  • 玩家移动时,敌人转向玩家,当偏离玩家的角度小于5度时,发射炮弹
  • 敌人血条显示在其上方,并且始终看向相机

三、界面组件信息

(1)游戏对象层级结构

Unity3D实现坦克大战_第2张图片

(2)组件参数信息

1.玩家Player组件参数

Name Type Position Rotation Scale Color
Player Empty (0, 0.25, -5) (0, 0, 0) (1, 1, 1) #228439
Button Cube (0, 0, 0) (0, 0, 0) (2, 0.5, 2) #228439
Top Cube (0, 0.5, 0) (0, 0, 0) (1, 0.5, 1) #228439
Gun Cylinder (0, 0, 1.5) (90, 0, 0) (0.2, 1, 0.4) #228439
FirePoint Empty (0, 1.15, 0) (0, 0, 0) (1, 1, 1) --

Player 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 1,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。 

2.玩家HP组件参数

Name Type Position Width/Height Color
PlayerHP Canvas (960, 540, 0) 1920/1080 --
Panel      Panel 位置信息全是0 #FFFFFF
HealthBG Image (-809,464,0) 200/20 #FFFFFF
Health Image (-809,464,0) 200/20 #FF2230

玩家 PlayerHP 的 Canvas 渲染模式是 Screen Space - Overlay。

制作一个红色的图片放入Health的Source Image中,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

3.敌人组件参数

Name Type Position Rotation Scale Color
Enemy Empty (0, 0.25, 5) (0, 180, 0) (1, 1, 1) #15D3F9
Button Cube (0, 0, 0) (0, 0, 0) (2, 0.5, 2) #15D3F9
Top Cube (0, 0.5, 0) (0, 0, 0) (1, 0.5, 1) #15D3F9
Gun Cylinder (0, 0, 1.5) (90, 0, 0) (0.2, 1, 0.4) #15D3F9
FirePoint Empty (0, 1.15, 0) (0, 0, 0) (1, 1, 1) --

Enemy 游戏对象添加了刚体组件,并修改 Mass = 100,Drag = 0.5,AngularDrag = 0.1,Freeze Rotation 中勾选 X 和 Z。  

4.敌人HP组件参数

Name Type Position Width/Height Color
HP Canvas (0, 0.85, 0) 2/0.2 --
HealthBG Image (0,0,0) 2/0.2 #FFFFFF
Health Image (0,0,0) 2/0.2 #FF2230

敌人 HP 的 Canvas 渲染模式是 World Space,将刚才的红色底图也放入Health的Source Image中,Health 的 ImageType 设置为 Filled,Fill Method 设置为 Horizontal。

5.地面和炮弹的组件参数

Name Type Position Rotation Scale Color
Plane Plane (0, 0, 0) (0, 0, 0) (10, 10, 10) GrassRockyAlbedo
Bullet Sphere (0, 0.5, -5) (0, 0, 0) (0.3, 0.3, 0.3) #228439

炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。

四、脚本代码:

1.CameraController

 CameraController 脚本组件挂在 MainCamera 游戏对象上。

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

public class CameraController : MonoBehaviour
{
    // Start is called before the first frame update
    private Transform player; // 玩家
    private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置
    private float targetDistance = 15f; // 相机看向玩家前方的位置

    void Start()
    {
        relaPlayerPos = new Vector3(0, 4, -8);
        player = GameObject.Find("Player/Top").transform; // 世界坐标系位置
    }

    private void LateUpdate()
    {
        ComCameraPos();
    }

    // 计算相机坐标
    private void ComCameraPos()
    {
        Vector3 target = player.position + player.forward * targetDistance;
        transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);
        transform.rotation = Quaternion.LookRotation(target - transform.position);
    }

    // 以origin为原点,已知vec在坐标轴locX/locY/locZ中的位置,将vec转为世界坐标系的位置
    private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX, Vector3 locY, Vector3 locZ) 
    { 
        return vec.x * locX + vec.y * locY + vec.z * locZ + origin;
        
    }
}

2.BulletInfo

using UnityEngine;

public class BulletInfo
{
    public string name; // 炮弹名
    public Color color; // 炮弹颜色
    public Vector3 flyDir; // 炮弹飞出方向
    public float speed; // 炮弹飞行速度
    public float fireRange; // 炮弹射程

    public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange)
    {
        this.name = name;
        this.color = color;
        this.flyDir = flyDir;
        this.speed = speed;
        this.fireRange = fireRange;
    }
}

3.BulletController

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

public class BulletController : MonoBehaviour
{
    private BulletInfo bulletInfo; // 炮弹信息
    private volatile bool isDying = false;

    // Start is called before the first frame update
    void Start()
    {
        gameObject.name = bulletInfo.name;
        GetComponent().material.color = bulletInfo.color;
        float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间
        Destroy(gameObject, lifeTime);
    }

    // Update is called once per frame
    void Update()
    {
        transform.GetComponent().velocity = bulletInfo.flyDir * bulletInfo.speed;
    }

    public void SetBulletInfo(BulletInfo bulletInfo)
    {
        this.bulletInfo = bulletInfo;
    }

    private void OnCollisionEnter(Collision other)
    {
        if (isDying)
        {
            return;
        }

        if(IsHitEnemy(gameObject.name, other.gameObject.name))
        {
            other.transform.Find("HP/Health").GetComponent().fillAmount -= 0.1f;
            isDying = true;
            Destroy(gameObject, 0.1f);
        }else if(IsHitPlayer(gameObject.name, other.gameObject.name))
        {
            GameObject.Find("PlayerHP/Panel/Health").GetComponent().fillAmount -= 0.1f;
            isDying = true;
            Destroy(gameObject, 0.1f);
        }
    }

    private bool IsHitEnemy(string name, string otherName)
    {
        return name.Equals("PlayerBullet") && otherName.Equals("Enemy");
    }

    private bool IsHitPlayer(string name, string otherName)
    {
        return name.Equals("EnemyBullet") && otherName.Equals("Player");
    }
}

4.PlayerController

PlayerController 脚本组件挂在 Player 游戏对象上。 

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

public class PlayerController : MonoBehaviour
{
    private Transform firePoint; // 开火点
    private GameObject bulletPrefab; // 炮弹预设体
    private float tankMoveSpeed = 4f; // 坦克移动速度
    private float tankRotateSpeed = 2f; // 坦克转向速度
    private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
    private float bulletCoolTime = 0.15f; // 炮弹冷却时间

    void Start()
    {
        firePoint = transform.Find("Top/Gun/FirePoint");
        bulletPrefab = (GameObject)Resources.Load("Prefabs/Bullet");
    }

    // Update is called once per frame
    void Update()
    {
        fireWaitTime += Time.deltaTime;
        float hor = Input.GetAxis("Horizontal");
        float ver = Input.GetAxis("Vertical");
        Move(hor, ver);
        if(Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
        {
            Fire();
        }

    }

    // 坦克移动
    private void Move(float hor, float ver) 
    {
        if (Mathf.Abs(hor) > 0.1f || Mathf.Abs(ver) > 0.1f) {
            GetComponent().velocity = transform.forward * tankMoveSpeed * ver;
            GetComponent().angularVelocity = Vector3.up * tankRotateSpeed * hor;
        }
    }

    // 开炮
    private void Fire()
    {
        if (fireWaitTime > bulletCoolTime) { 
            BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);
            GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);
            bullet.AddComponent().SetBulletInfo(bulletInfo);
            fireWaitTime = 0f;
        }
    }
}

5.EnemyController

EnemyController 脚本组件挂在 Enemy 游戏对象上。  

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

public class EnemyController : MonoBehaviour
{
    private Transform target; // 目标
    private Transform top; // 炮头
    private Transform firePoint; // 开火点
    private Transform hp; // 血条
    private GameObject bulletPrefab; // 炮弹预设体
    private float rotateSpeed = 0.4f; // 坦克转向速度
    private float fireWaitTime = float.MaxValue; // 距离上次开火已等待时间
    private float bulletCoolTime = 1f; // 炮弹冷却时间

    // Start is called before the first frame update
    void Start()
    {
        target = GameObject.Find("Player/Top").transform;
        top = transform.Find("Top");
        firePoint = transform.Find("Top/Gun/FirePoint");
        hp = transform.Find("HP");
        bulletPrefab = (GameObject)Resources.Load("Prefabs/Bullet");
    }

    // Update is called once per frame
    void Update()
    {
        fireWaitTime += Time.deltaTime;
        if (LookAtTarget()) {
            Fire();
        }
        HPLookAtCamera();
    }

    private bool LookAtTarget()
    {
        Vector3 dir = target.position - top.position;
        float angle = Vector3.Angle(dir, top.forward);
        if(angle > 5)
        {
            int axis = Vector3.Dot(Vector3.Cross(dir, top.forward), Vector3.up) > 0 ? -1 : 1;
            GetComponent().angularVelocity = axis * Vector3.up * rotateSpeed;
            return false;
        }
        GetComponent().velocity = Vector3.zero;
        return true;
    }

    private void HPLookAtCamera()
    {
        Vector3 cameraPos = Camera.main.transform.position;
        Vector3 target = new Vector3(cameraPos.x, hp.position.y, cameraPos.z);
        hp.LookAt(target);
    }

    private void Fire()
    {
        if(fireWaitTime > bulletCoolTime)
        {
            BulletInfo bulletInfo = new BulletInfo("EnemyBullet", Color.yellow, top.forward, 5f, 10f);
            GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity); // 通过预设体创建炮弹
            bullet.AddComponent().SetBulletInfo(bulletInfo);
            fireWaitTime = 0;
        }
    }

}


你可能感兴趣的:(游戏,数码相机)