【坦克大戰】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.鼠標右鍵轉向