3D坦克大战

游戏规则:

①玩家1和玩家2分别控制自己的坦克,保护己方坦克同时尝试去摧毁对方坦克,存活的一方胜利。

②玩家1 WASD控制移动,space开火;玩家2 上下左右控制移动,右enter开火。

③玩家们都不可以离开地图范围。

④地图中的建筑物会阻碍坦克的行动。

项目资源

BatallaCL/MyUnity at master (github.com)icon-default.png?t=N7T8https://github.com/BatallaCL/MyUnity/tree/master

游戏截图

 

游戏架构

使用了MVC架构。

  • 场景中的所有GameObject就是Model,它们来自于Models(已经建好的模型)、Prefabs(已经建好的prefab)、AudioClip(音频文件)等文件夹(下面也会在用到具体文件时在详述),并且都受到Script中文件的控制,比如说坦克的开火交互受到FireAttack的控制,MainCamera受到FollowPlayer的控制……
  • 控制文件都放在了Script文件夹中

游戏搭建

        场景设置

        首先,在Prefabs中将LevelArt加入到Hierarchy中,可以在Scene处看到生成的LevelArt(制作好的关卡地图模型)。这里可以利用Models中的模型对关卡中的房屋树木等障碍物做任意自己喜欢的设置,也可以调整Directional Light或在Edit→ProjectSettings→Lighting中设置场景光亮、背景颜色等。

        同样将一个Models中的Tank加入到Hierarchy中,这会在LevelArt上直接生成一个Tank,这里可以对Tank除高度外的坐标做任意调整。

        区分玩家按键

        因为笔者搭建的3D大战是双人游戏,所以为了区分玩家按键需要进行以下设置:在Edit→ProjectSettings→Input Manager中复制Horizontal和Vertical并修改名字和按键。

        笔者修改后的设置如图:

       

        Tank移动

        注:可以将Scene中的Tank加入到Prefabs中,此后对Tank进行的任意修改都可以直接Overrides→Apply All应用到Prefabs Tank中,之后加入Tank(添加玩家时)则更加便捷。

        Tank→Add Component→Box Collider(使得Tank模型能够与LevelArt中的对象碰撞,从而起到障碍物的作用)

        Tank→Add Component→Rigidbody(用于设置前后移动,勾选Free Position Y、Free RotationX、Free Rotation Z,使得Tank只能在X、Z方向移动,只能绕Y轴旋转)

        Tank→Add Component→Tank Movement(Script)

        Tank Movement的代码如下:(这里用到了上面所区分的玩家按键,RigidBody根据按键对应的玩家,控制其Tank的移动和旋转)

// Tank Movement
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class tankMovement : MonoBehaviour
{
    private Rigidbody rigidbody;
    public float id = 1; // 玩家编号 区分控制
    // Start is called before the first frame update
    void Start()
    {
        rigidbody = this.GetComponent<Rigidbody>();
    }

    private void FixedUpdate()// 在固定帧调用 可放置位移操作
    {
        float v = Input.GetAxis("VerticalPlayer"+id); // 获取前后位置 -1到1对应向后到向前
        rigidbody.velocity = transform.forward * v * 8; // 前后移动 速度为8单位每秒

        float h = Input.GetAxis("HorizontalPlayer"+id); // 获取水平位置
        rigidbody.angularVelocity = transform.up * h * 8; // 围绕y轴旋转8单位
    }
}

        完成好上述内容后,玩家就可以在运行后操控Tank进行移动和旋转了。 

        

        Tank开火

        在Tank对象中添加一个empty对象,并更名为FirePosition用于Tank开火后Shell(子弹)的生成。

        Tank→Add Component→Fire Attack(Script)

// FireAttack.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireAttack : MonoBehaviour
{
    public GameObject shell; // 用于应用子弹实例
    public KeyCode FireKey = KeyCode.Space; // 发射键位设置 默认空格键
    private Transform FirePosition; // 发射位置
    public float shellspeed = 15;
    // Start is called before the first frame update
    void Start()
    {
        FirePosition = transform.Find("FirePosition"); // 获取发射位置 应用之前创建的empty对象
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(FireKey)) // 按下keycode时
        {
            GameObject go = GameObject.Instantiate(shell, FirePosition.position, FirePosition.rotation) as GameObject; // 实例化子弹到fireposition位置 旋转角度也保持一致
            go.GetComponent<Rigidbody>().velocity = go.transform.forward * shellspeed; // 给予子弹向前发射的速度
        }
    }
}

       

        子弹的生成、碰撞和销毁

        将Shell加入到Prefabs中,做以下设置:

        Shell→Add Component→Capsule Collider→勾选Is Trigger(用于子弹碰撞处理)

        Shell→Add Component→Rigidbpdy(用于子弹移动)

        Shell→Add Component→Shell(Script 子弹碰撞处理脚本文件)

        Tank→Add Component→Add Tag(加标签Tank,用于子弹识别碰撞物体是否是Tank)

        ShellExplosion(Prefabs中制作好的子弹销毁特效)→Add Component→ShellExplosionDestory(script,用于特效结束后销毁对象)

// Shell(Script)
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class shell : MonoBehaviour
{
    public GameObject shellExplosionPrefab; // 用于调用子弹销毁特效
    public float time; // 子弹销毁时间设置
    // Start is called before the first frame update
    void Start()
    {
        Destroy(this.gameObject, time);
    }

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

    public void OnTriggerEnter(Collider collider) // 子弹碰撞事件 必须打开shell对象的碰撞检测(is trigger) 必须打开shellexplos的play on awake使其在实例化后自动播放
    {
        GameObject.Instantiate(shellExplosionPrefab, transform.position, transform.rotation); // 实例化子弹爆炸特性
        //GameObject.Destroy(this.gameObject); // 销毁子弹对象 该版本会自动销毁 所以注销掉了

        if(collider.tag == "Tank")
        {
          // 调用碰撞对象的TankDamage函数
         collider.SendMessage("TankDamage",null,SendMessageOptions.DontRequireReceiver);
        }
    }
}
// ShellExplosionDestory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShellExplosionDestroy : MonoBehaviour
{
    public float time; // 设置为该特效的播放时间1.5s

    // Start is called before the first frame update
    void Start()
    {
        Destroy(this.gameObject,time);
    }

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

        玩家生命(TankHealth)

        玩家生命(TankHealth)决定了Tank的销毁和游戏的胜负,Tank在受到子弹碰撞后需要扣除一定生命值,并在扣除的生命值达到一定水平后销毁该对象,而对方玩家胜利。

        Tank→Add Component→TankHealth(script,用于玩家生命处理)

// TankHealth.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TankHealth : MonoBehaviour
{
    public int hp = 100;
    public GameObject tankExplosion;

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

    // Update is called once per frame
    void Update()
    {
        if (hp <= 0) // 当坦克受到致命伤后 触发tankExplosion特效(同样要记得勾选play on awake)
        {
            // 播放tankExplosion特效
            GameObject.Instantiate(tankExplosion, transform.position + Vector3.up, transform.rotation);
            GameObject.Destroy(this.gameObject);
        }
    }

    void TankDamage()
    {
        if (hp <= 0) return;
        hp -= Random.Range(10, 20);
    }
}

        添加第二个玩家

        前文已经完成了第二个玩家按键、玩家Id等的设置,这里只需要再加入一个tank对象,修改TankMovement的number值为2区别操作键位 ,修改FireAttack文件的fire key为任意自己喜欢的键位即可。

        现在两个坦克一模一样,不太还区分玩家所操作的对象。为了更好的区分两个Tank, 可以用material材质替换tank模型各个组件中的mesh renderer的颜色材质。

        视角跟随

        原有的Main Camera对象是静止的,没法随Tank移动而移动,这也意味着玩家随时可能会丢失视野。所以,游戏还需要设置视角跟随。

        Main Camera→Add  Component→ FollowPlayer(Script,用于视角跟随)

// FollowPlayer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FollowPlayer : MonoBehaviour
{
    public Transform player1;
    public Transform player2;

    private Vector3 offset;
    private Camera camera;
    // Start is called before the first frame update
    void Start()
    {
        offset = transform.position - (player1.position + player2.position) / 2;
        camera = this.GetComponent<Camera>();
    }

    // Update is called once per frame
    void Update()
    {
        if (player1 == null || player2 == null) return;// 有游戏玩家死亡则停止跟随
        transform.position = (player1.position + player2.position) / 2 + offset;
        float distance = Vector3.Distance(player1.position, player2.position);
        float size = distance * 0.58f;
        camera.orthographicSize = size;
    }
}

        那么,现在3D坦克大战的核心功能已经完成了,玩家可以操作两个Tank进行对战角逐胜利。

        背景音乐

        为了给游戏添加一点激情,简单添加了一个背景音乐,具体步骤如下:

        添加empty对象(更名为GameManager)→Add Component→Audio Source→AudioClip替换为BackGroundMusic(AudioClips文件夹中)→勾选paly on awake和loop

        现在运行游戏就可以听到derderder的背景音乐了。

可以做的扩展:

  • 添加游戏分出胜负后,用户能够直接Restart的设置。
  • 制作出更多地图,并且关卡化。
  • 在地图中随机投放不同增益效果的道具,增强游戏的多样性和趣味性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值