【坦克大战】Unity3D多人在线游戏(泰课的坦克大战--旋转的螺丝钉)

 

【坦克大战】Unity3D多人在线游戏

http://www.taikr.com/my/course/937

1.NetworkManager的介绍:

 

说明:选择固定生成时会自动寻找有StartPosition组件的位置

2.NetWorkDiscovery组件的介绍:

使用在局域网中的一个组件,在英特网上不能使用

官方文档:

 

说明:NetWorkDiscovery与Network managerHUD相似:

Network managerHUD介绍:就是显示Network manager的,如下图:

 

 

3.框架部分,开始界面的5个按钮

 

IndexUI

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class IndexUI : MonoBehaviour {

 

    public void SingleBtn()

    {

 

    }

    public void MutiplayBtn()

    {

 

    }

    public void LanBtn()

    {

 

    }

}

4.LanGame局域网对战功能的实现

 

放在NetWorkManagerCustom中的代码:

public static void LanGame()

    {

        //转换一下再调用

        singleton.StartCoroutine((singleton as NetWorkManagerCustom).DiscoveryNetWork());

    }

 

    public IEnumerator DiscoveryNetWork()

    {

        //取得Discovery组件

        NetworkDiscoverCustom discovery = GetComponent<NetworkDiscoverCustom>();

        discovery.Initialize();//组件初始化

        discovery.StartAsClient();//扫描局域网的服务器

        yield return new WaitForSeconds(2);

 

        //没有找到局域网中的服务器的话就建立服务器

        if (discovery.running)

        {

            discovery.StopBroadcast();//停掉广播包

            yield return new WaitForSeconds(0.5f);

 

            discovery.StartAsServer();//作为服务器发射广播包

            StartHost();//作为服务器和客户端同时启动

            //StartClient();//作为客户端启动

            //StartServer();//只作为服务器启动

        }

}

放在NetworkDiscoverCustom中的代码:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.Networking;

 

public class NetworkDiscoverCustom : NetworkDiscovery {

 

    public override void OnReceivedBroadcast(string fromAddress,string data)

    {

        StopBroadcast();

 

        NetWorkManagerCustom.singleton.networkAddress = fromAddress;

        NetWorkManagerCustom.singleton.StartClient();

    }

}

 

5.NetGame,英特网的在线对战:

 

 

public static void NetGame()

    {

        singleton.StartMatchMaker();//表示启用Unet网络对战功能

        singleton.matchMaker.ListMatches(0, 20, "", false, 0, 0, singleton.OnMatchList);

            //第1个参数:startpagenumber:表示第几页的list

            //第2个参数:表示每一页有多少个

            //第3个参数:表示需要找到的房间名称

            //第4个参数:表示是否返回带有密钥的房间

            //第5个参数:表示的是竞赛方面的设置

            //第6个参数:表示的是一个域,只能从这个域上面返回房间

            //第7个参数:是一个回调的函数

    }

 

    public override void OnMatchList(bool success, string extendedInfo, List<MatchInfoSnapshot> matchList)

    {

        if (!success) return;

 

        if (matchList != null)

        {

            List<MatchInfoSnapshot> availableMatches = new List<MatchInfoSnapshot>();

            foreach (MatchInfoSnapshot match in matchList)

            {

                if (match.currentSize < match.maxSize)

                {

                    availableMatches.Add(match); //保存房间玩家没有满的情况

                }

 

            }

 

            //列表的数量是否为0,为0创建服务器,不为0的话就加入服务器

            if (availableMatches.Count == 0)

            {

                //创建服务器

                CreateMatch();

            }

            else

            {

                //加入服务器

                matchMaker.JoinMatch(availableMatches[Random.Range(0, availableMatches.Count - 1)].networkId, "", "", "", 0, 0, OnMatchJoined);

            }

 

        }

    }

 

    void CreateMatch() //告诉Unet创建网络服务器

    {

        matchMaker.CreateMatch("", matchSize, true, "", "", "", 0, 0, OnMatchCreate);

        //第1个参数:房间名称

        //第2个参数:房间可玩家数

        //第3个参数:

        //第4个参数:口令

        //第5个参数:Client Ip地址(公网)

        //第6个参数:私网地址

    }

 

    public override void OnMatchCreate(bool success, string extendedInfo, MatchInfo matchInfo)

    {

        if (!success) return;

        StartHost(matchInfo);//利用Unet返回的matchinfo创建服务器

    }

 

    public override void OnMatchJoined(bool success, string extendedInfo, MatchInfo matchInfo)

    {

        if (!success)

        {

            int currentScene = SceneManager.GetActiveScene().buildIndex;

            SceneManager.LoadScene(currentScene);

            return;

        }

        StartClient(matchInfo); //利用Unet传回的matchinfo启动客户端

    }

 

6.单人模式:

public static void SimpleGame()

    {

        singleton.StartHost(singleton.connectionConfig, 1);

 }

 

7.NetWorkTransform

8、角色的移动

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

public class Player : NetworkBehaviour {

    private Rigidbody rb;

    public float MoveSpeed = 8f;

    void Awake () {
        rb = transform.GetComponent<Rigidbody>();
    }
    
    void FixedUpdate () {
        if (!isLocalPlayer) return;//保证只对本地角色进行控制

        Vector2 moveDir;
        if (Input.GetAxisRaw("Horizontal") == 0 && Input.GetAxisRaw("Vertical") == 0)
        {
            moveDir.x = 0;
            moveDir.y = 0;

        }
        else
        {
            //可以移动
            moveDir.x = Input.GetAxis("Horizontal");
            moveDir.y = Input.GetAxis("Vertical");
            Move(moveDir);
        }

    }
    private void Move(Vector2 direction=default(Vector2))
    {
        if (direction != Vector2.zero)
        {
            //转方向
            transform.rotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y));
            //计算前方的点
            Vector3 movementDir = transform.forward * MoveSpeed * Time.deltaTime;
            //移动到这个点
            rb.MovePosition(rb.position + movementDir);
        }
    }
}
9.摄像机跟随

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

public class CameraFollow : MonoBehaviour {

    public Transform target;
    public float distance = 10f;
    public float height = 5f;

    void Update()
    {
        if (!target) return;

        Quaternion currentRotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);
        Vector3 pos = target.position;//target的坐标
        pos -= currentRotation * Vector3.forward * Mathf.Abs(distance);//距离
        pos.y = target.position.y + Mathf.Abs(height);//高度
        transform.position = pos;

        transform.LookAt(target);
        //下面这一句其实要不要无所谓,只是为了精确
        transform.position = target.position - (transform.forward * Mathf.Abs(distance));
    }
}
在player中重写生成本地物体时调用的方法,把坦克设置为摄像机的target

 //把摄像机的target用代码设置为坦克的--这里挺重要的,在OnStartLocalPlayer()方法里面写
    public override void OnStartLocalPlayer()
    {
        Camera.main.GetComponent<CameraFollow>().target = transform;
    }

 10.炮台的转动及炮台转动的同步

 

 [HideInInspector]   //在面板上隐藏公有值
    [SyncVar(hook = "OnTurretRotation")]//turretRotation同步到所有客户端,并调用OnTurretRotation()方法   
    public int turretRotation;

 l

//把输入的屏幕坐标转为ray射线
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Plane plane = new Plane(Vector3.up, Vector3.up);//虚构的平面
        float distance = 0f;//屏幕宽度
        Vector3 hitPos = Vector3.zero;//设置打击点,取默认值

        if(plane.Raycast(ray,out distance))
        {
            hitPos = ray.GetPoint(distance) - transform.position;
        }
        RotateTurret(new Vector2(hitPos.x, hitPos.z));

/// <summary>
    /// 炮台旋转
    /// </summary>
    /// <param name="direction"></param>
    void RotateTurret(Vector2 direction = default(Vector2))
    {
        if (direction == Vector2.zero) return;

        int newRotation = (int)(Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y)).eulerAngles.y);
        turret.rotation = Quaternion.Euler(0, newRotation, 0);

        turretRotation = newRotation;
        
    }

    [Command]
    void CmdRotateTurret(int value)
    {
        turretRotation = value;//调用服务器这个值得改变
    }

    void OnTurretRotation(int value)
    {
        if (isLocalPlayer) return;

        turretRotation = value;
        turret.rotation = Quaternion.Euler(0, turretRotation, 0);//进行旋转
    }

11.鼠标右键转向