unity3d-多人坦克对战

这次的作业是在上一次坦克大战作业的基础上进行改进,实现多人网络对战版。

视频演示

https://www.bilibili.com/video/av25649733/

游戏实现

游戏主要对象还是利用了坦克预设和子弹预设。如下图:

unity3d-多人坦克对战_第1张图片

将原本的脚本都去掉,然后为tank预设添加Network Identity和Network Transform组件,并且把Network Identity的local player authority选项勾上,以允许能够在本地进行控制。然后创建一个Empty对象并挂载Network Manager和Network Manager HUD脚本,作为整个游戏的网络管理对象。同样的道理,子弹也应该添加Network Identity和Network Transform组件,注意子弹不需要勾选local player authority,并且Network Send Rate设为0即可。最后还要在Network Manager的Spawn Info里面注册相应的预设。具体设置如下:

unity3d-多人坦克对战_第2张图片
unity3d-多人坦克对战_第3张图片
unity3d-多人坦克对战_第4张图片

主要代码

为坦克添加控制脚本PlayerMove。Update()函数首先判断是不是本地对象执行,只有是服务端对象执行才可以修改坦克的位置,通过监听按键输入来控制刚体组件相应的速度来实现。当按下空格的时候,坦克开火。我使用了AddForce来控制子弹实例的刚体运动。如果直接在本地执行函数CmdShoot中使用,会出现client端子弹产生后不会运动的效果。(原因可以查一下谷歌:unity3d network transform addforce not working)。这里需要将AddForce函数放在一个ClientRpc函数中调用,这样才可以在所有客户端中同步。

其他的代码比较简单,这里就不累述。注意下两个函数:OnStartClient()和OnStartServer()。这两个函数分别在客户端对象和服务端对象产生时调用,这里我使用它们的目的是将对象的transform传递给main camera,使得main camera可以根据坦克对象的位置自动更新视野。

public class PlayerMove : NetworkBehaviour
{
    public GameObject bulletPrefabs;

    public override void OnStartClient()
    {
        Camera.main.GetComponent().setTarget(this.transform);
    }

    public override void OnStartServer()
    {
        Camera.main.GetComponent().setTarget(this.transform);
    }

    private void FixedUpdate()
    {
        if (!isLocalPlayer)
            return;

        float offsetX = Input.GetAxis("Horizontal");
        float y = this.transform.localEulerAngles.y + offsetX * 5;
        float x = this.transform.localEulerAngles.x;
        this.transform.localEulerAngles = new Vector3(x, y, 0);
    }

    // Update is called once per frame
    void Update () {
        if (!isLocalPlayer)
            return;

        if (Input.GetKey(KeyCode.W))
        {
            this.GetComponent().velocity = this.transform.forward * 20;
        }
        if (Input.GetKey(KeyCode.S))
        {
            this.GetComponent().velocity = this.transform.forward * -20;
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdShoot();
        }
    }

    [Command]
    void CmdShoot()
    {
        var bullet = (GameObject)Instantiate(
            bulletPrefabs,
            new Vector3(transform.position.x, 1.5f, transform.position.z) + transform.forward * 1.5f,
            Quaternion.identity);

        bullet.transform.forward = transform.forward;

        NetworkServer.Spawn(bullet);
        RpcAddForceOnAll(bullet);

        // make bullet disappear after 2 seconds
        Destroy(bullet, 2.0f);

    }

    [ClientRpc]
    void RpcAddForceOnAll(GameObject bullet)
    {
        bullet.GetComponent().AddForce(bullet.transform.forward * 20, ForceMode.Impulse);
    }
}

Combat组件用于控制坦克血量,health字段设置为SyncVar,表示该变量是服务端同步的。扣血函数TakeDamage只有服务端才可以调用,并且当玩家坦克血量为0之后(这里我设定为承受5次伤害即死亡),会进行重生,RpcRespawn函数会在客户端对象中更新玩家坦克的位置。

public class Combat : NetworkBehaviour
{

    public const int maxHealth = 500;

    [SyncVar]
    public int health = maxHealth;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        health -= amount;
        Debug.Log("health value = " + health.ToString());
        if (health <= 0)
        {
            health = maxHealth;
            RpcRespawn();
        }
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            transform.position = Vector3.zero;
        }
    }
}

BulletManager 用于管理子弹的状态,当子弹发生碰撞或者子弹落到地上都会消失。如果击中敌军坦克还会对其进行扣血。

public class BulletManager : MonoBehaviour {

    //private float explosionRadius = 3f;

    private void FixedUpdate()
    {
        if(this.transform.position.y <= 0)
        {
            Destroy(gameObject);
        }
    }

    void OnCollisionEnter(Collision other)
    {
        var hit = other.gameObject;
        var hitPlayer = hit.GetComponent();

        if (hitPlayer != gameObject)
        {
            // 根据击中坦克与爆炸中心的距离计算伤害值
            //float distance = Vector3.Distance(colliders[i].transform.position, transform.position);//被击中坦克与爆炸中心的距离
            //float hurt = 100f / distance;
            //float current = colliders[i].GetComponent().getHp();
            //colliders[i].GetComponent().setHp(current - hurt);
            var combat = hit.GetComponent();
            combat.TakeDamage(100);
        }
        Destroy(gameObject);
    }
}

总结

完整代码请到我的github查看:https://github.com/CarolSum/Unity3d-Learning/tree/master/hw10

老师课上的简单多人游戏教程很详细,参考意义很大~这次作业也比较简单,能做出一个小型多人对战游戏很不错~毕竟小时候也是玩这种游戏大的。但是这远远不够,想要做出更有意义的游戏还是得继续深入学习,当然这得放到期末考之后啦~

你可能感兴趣的:(Unity3D,unity3d,多人坦克对战)