This commit is contained in:
2025-09-17 18:56:28 +08:00
commit 54c72710a5
5244 changed files with 5717609 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
using UnityEngine;
namespace Mirror.Examples.TanksHybrid
{
public class Projectile : NetworkBehaviour
{
public float destroyAfter = 2f;
public Rigidbody rigidBody;
public float force = 1000f;
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 other)
{
Debug.Log("Hit: " + other.name);
if (other.transform.parent.TryGetComponent(out Tank tank))
{
--tank.health;
if (tank.health == 0)
NetworkServer.RemovePlayerForConnection(tank.netIdentity.connectionToClient, RemovePlayerOptions.Destroy);
DestroySelf();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f8639b3a1d2f431586ca8b52bb1337b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using UnityEngine;
using UnityEngine.AI;
namespace Mirror.Examples.TanksHybrid
{
public class Tank : NetworkBehaviour
{
[Header("Components")]
public NavMeshAgent agent;
public Animator animator;
public TextMesh healthBar;
public Transform turret;
[Header("Movement")]
public float rotationSpeed = 100;
[Header("Firing")]
public KeyCode shootKey = KeyCode.Space;
public GameObject projectilePrefab;
public Transform projectileMount;
[Header("Stats")]
public int health = 5; // not [SyncVar] because Weaver doesn't work with Hybrid yet. instead we sync it manually below.
int lastHealth = 5;
// naming for easier debugging
public override void OnStartClient()
{
name = $"Player[{netId}|{(isLocalPlayer ? "local" : "remote")}]";
}
public override void OnStartServer()
{
name = $"Player[{netId}|server]";
}
void Update()
{
// manual setdirty test
if (health != lastHealth)
{
SetDirty();
lastHealth = health;
}
// always update health bar.
// (SyncVar hook would only update on clients, not on server)
healthBar.text = new string('-', health);
// take input from focused window only
if(!Application.isFocused) return;
// movement for local player
if (isLocalPlayer)
{
// 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();
}
RotateTurret();
}
}
// this is called on the server
[Command]
void CmdFire()
{
GameObject projectile = Instantiate(projectilePrefab, projectileMount.position, projectileMount.rotation);
NetworkServer.Spawn(projectile);
RpcOnFire();
}
// this is called on the tank that fired for all observers
[ClientRpc]
void RpcOnFire()
{
animator.SetTrigger("Shoot");
}
//[ServerCallback]
//void OnTriggerEnter(Collider other)
//{
// if (other.GetComponent<Projectile>() != null)
// {
// --health;
// if (health == 0)
// NetworkServer.Destroy(gameObject);
// }
//}
void RotateTurret()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, 100))
{
Debug.DrawLine(ray.origin, hit.point);
Vector3 lookRotation = new Vector3(hit.point.x, turret.transform.position.y, hit.point.z);
turret.transform.LookAt(lookRotation);
}
}
// Health is serialized/deserialized manually.
// setting the component to SyncMethod=Unreliable automatically takes care of the rest.
public override void OnSerialize(NetworkWriter writer, bool initialState)
{
// Debug.LogWarning($"Tank {name} OnSerialize {(initialState ? "full" : "delta")} health={health}");
writer.WriteInt(health);
}
public override void OnDeserialize(NetworkReader reader, bool initialState)
{
health = reader.ReadInt();
// Debug.LogWarning($"Tank {name} OnDeserialize {(initialState ? "full" : "delta")} health={health}");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f45f3637f32e4490e8bf0a852cbcb0e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: