示例就是最好的學習!
Tank
要做的游戲也是與射擊相關(guān)的,就從Tank開始學習吧!看看哪些可以用上,哪些需要改進!

Scripts
Tank.cs
public class Tank : NetworkBehaviour
{
[Header("Components")]
public NavMeshAgent agent;
public Animator animator;
[Header("Movement")]
public float rotationSpeed = 100;
[Header("Firing")]
public KeyCode shootKey = KeyCode.Space;
public GameObject projectilePrefab;
public Transform projectileMount;
void Update()
{
// movement for local player
if (!isLocalPlayer) return;
// rotate
float horizontal = Input.GetAxis("Horizontal");
transform.Rotate(0, horizontal * rotationSpeed * Time.deltaTime, 0);
// move
float vertical = Input.GetAxis("Vertical");
Vector3 forward = transform.TransformDirection(Vector3.forward);
agent.velocity = forward * Mathf.Max(vertical, 0) * agent.speed;
animator.SetBool("Moving", agent.velocity != Vector3.zero);
// shoot
if (Input.GetKeyDown(shootKey))
{
CmdFire();
}
}
// this is called on the server
[Command]
void CmdFire()
{
GameObject projectile = Instantiate(projectilePrefab, projectileMount.position, transform.rotation);
NetworkServer.Spawn(projectile);
RpcOnFire();
}
// this is called on the tank that fired for all observers
[ClientRpc]
void RpcOnFire()
{
animator.SetTrigger("Shoot");
}
}
網(wǎng)絡(luò)對象繼承于NetworkBehaviour,而不是MonoBehaviour。
Update()
Update()函數(shù)中檢測輸入,水平軸是旋轉(zhuǎn),由rotationSpeed控制旋轉(zhuǎn)速度。
*我們的游戲是需要旋轉(zhuǎn)的時候保持原移動方向的慣性,例如在太空中轉(zhuǎn)彎的感覺,與這個速度是不一樣的。
TransformDirection轉(zhuǎn)換到世界坐標系,更多參見1、2
如果按下“開火”鍵,則調(diào)用CmdFire()函數(shù)。
CmdFire()
這是一個[Command]修飾的函數(shù),指明客戶端命令。
Instantiate()復制一個子彈預(yù)制件對象,查看Instantiate用法。
子彈是網(wǎng)絡(luò)對象,由NetworkServer.Spawn()生成,查看NetworkServer。
RpcOnFire()
這是一個[ClientRpc]修飾的函數(shù),可以由服務(wù)端調(diào)用。
Projectile.cs
public class Projectile : NetworkBehaviour
{
public float destroyAfter = 5;
public Rigidbody rigidBody;
public float force = 1000;
public override void OnStartServer()
{
Invoke(nameof(DestroySelf), destroyAfter);
}
// set velocity for server and client. this way we don't have to sync the
// position, because both the server and the client simulate it.
void Start()
{
rigidBody.AddForce(transform.forward * force);
}
// destroy for everyone on the server
[Server]
void DestroySelf()
{
NetworkServer.Destroy(gameObject);
}
// ServerCallback because we don't want a warning if OnTriggerEnter is
// called on the client
[ServerCallback]
void OnTriggerEnter(Collider co)
{
NetworkServer.Destroy(gameObject);
}
}
OnStartServer()
Invoke()方法,5秒后銷毀自己DestroySelf()
Start()
Addforce直接模仿物理受力了,給物體施加一個力,也會收到其他力的作用
Pong

Scripts
NetworkManagerPong.cs
public class NetworkManagerPong : NetworkManager
{
public Transform leftRacketSpawn;
public Transform rightRacketSpawn;
GameObject ball;
public override void OnServerAddPlayer(NetworkConnection conn)
{
// add player at correct spawn position
Transform start = numPlayers == 0 ? leftRacketSpawn : rightRacketSpawn;
GameObject player = Instantiate(playerPrefab, start.position, start.rotation);
NetworkServer.AddPlayerForConnection(conn, player);
// spawn ball if two players
if (numPlayers == 2)
{
ball = Instantiate(spawnPrefabs.Find(prefab => prefab.name == "Ball"));
NetworkServer.Spawn(ball);
}
}
public override void OnServerDisconnect(NetworkConnection conn)
{
// destroy ball
if (ball != null)
NetworkServer.Destroy(ball);
// call base functionality (actually destroys the player)
base.OnServerDisconnect(conn);
}
}
繼承自NetworkManager,主要用來處理玩家位置。
OnServerAddPlayer()函數(shù)根據(jù)是1個還是2個玩家數(shù),設(shè)置左邊或右邊,如果2個玩家就生成小球。
OnServerDisconnect()函數(shù)銷毀自己創(chuàng)建的對象。
Players.cs
public class Player : NetworkBehaviour
{
public float speed = 30;
public Rigidbody2D rigidbody2d;
// need to use FixedUpdate for rigidbody
void FixedUpdate()
{
// only let the local player control the racket.
// don't control other player's rackets
if (isLocalPlayer)
rigidbody2d.velocity = new Vector2(0, Input.GetAxisRaw("Vertical")) * speed * Time.fixedDeltaTime;
}
}
FixedUpdate()函數(shù)僅僅控制拍子方向。
Ball.cs
public class Ball : NetworkBehaviour
{
public float speed = 30;
public Rigidbody2D rigidbody2d;
public override void OnStartServer()
{
base.OnStartServer();
// only simulate ball physics on server
rigidbody2d.simulated = true;
// Serve the ball from left player
rigidbody2d.velocity = Vector2.right * speed;
}
float HitFactor(Vector2 ballPos, Vector2 racketPos, float racketHeight)
{
// ascii art:
// || 1 <- at the top of the racket
// ||
// || 0 <- at the middle of the racket
// ||
// || -1 <- at the bottom of the racket
return (ballPos.y - racketPos.y) / racketHeight;
}
[ServerCallback] // only call this on server
void OnCollisionEnter2D(Collision2D col)
{
// Note: 'col' holds the collision information. If the
// Ball collided with a racket, then:
// col.gameObject is the racket
// col.transform.position is the racket's position
// col.collider is the racket's collider
// did we hit a racket? then we need to calculate the hit factor
if (col.transform.GetComponent<Player>())
{
// Calculate y direction via hit Factor
float y = HitFactor(transform.position,
col.transform.position,
col.collider.bounds.size.y);
// Calculate x direction via opposite collision
float x = col.relativeVelocity.x > 0 ? 1 : -1;
// Calculate direction, make length=1 via .normalized
Vector2 dir = new Vector2(x, y).normalized;
// Set Velocity with dir * speed
rigidbody2d.velocity = dir * speed;
}
}
}
OnStartServer()
初始球方向
HitFactor()
判斷是否打在球拍上
OnCollisionEnter2D()
碰撞處理
Scene

Table對象由WallTop等5個對象組成,其中除DottleLine外,都掛載了Box Collider 2D組件,支持碰撞檢測。

整體流程
- 場景初始界面,直接設(shè)計;(我們的游戲可能需要能動態(tài)下載布局生成場景)
- Player預(yù)制,在NetworkManager中生成Player對象;
- Player腳本中支持用戶輸入,響應(yīng)用戶操作。