Boss Room 是一款使用 Unity MLAPI 制作的全功能合作多人 RPG。 它旨在作为学习样本,展示类似游戏中经常出现的某些典型游戏模式。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

117 行
4.8 KiB

using MLAPI;
using UnityEngine;
namespace BossRoom.Client
{
/// <summary>
/// Captures inputs for a character on a client and sends them to the server.
/// </summary>
[RequireComponent(typeof(NetworkCharacterState))]
public class ClientInputSender : NetworkedBehaviour
{
private const float k_MouseInputRaycastDistance = 100f;
// Cache raycast hit array so that we can use non alloc raycasts
private readonly RaycastHit[] k_CachedHit = new RaycastHit[1];
// This is basically a constant but layer masks cannot be created in the constructor, that's why it's assigned int Awake.
private LayerMask k_MouseQueryLayerMask;
private NetworkCharacterState m_NetworkCharacter;
/// <summary>
/// We detect clicks in Update (because you can miss single discrete clicks in FixedUpdate). But we need to
/// raycast in FixedUpdate, because raycasts done in Update won't work reliably.
/// This nullable vector will be set to a screen coordinate when an attack click was made.
/// </summary>
private System.Nullable<Vector3> m_AttackClickRequest;
public override void NetworkStart()
{
// TODO Don't use NetworkedBehaviour for just NetworkStart [GOMPS-81]
if (!IsClient || !IsOwner)
{
enabled = false;
}
}
void Awake()
{
m_NetworkCharacter = GetComponent<NetworkCharacterState>();
k_MouseQueryLayerMask = LayerMask.GetMask(new[] {"Ground", "PCs", "NPCs"});
}
void FixedUpdate()
{
// TODO replace with new Unity Input System [GOMPS-81]
// Is mouse button pressed (not just checking for down to allow continuous movement inputs by holding the mouse button down)
if (Input.GetMouseButton(0))
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.RaycastNonAlloc(ray, k_CachedHit, k_MouseInputRaycastDistance, k_MouseQueryLayerMask) > 0)
{
// The MLAPI_INTERNAL channel is a reliable sequenced channel. Inputs should always arrive and be in order that's why this channel is used.
m_NetworkCharacter.InvokeServerRpc(m_NetworkCharacter.SendCharacterInputServerRpc, k_CachedHit[0].point, "MLAPI_INTERNAL");
}
}
if (m_AttackClickRequest != null)
{
var ray = Camera.main.ScreenPointToRay(m_AttackClickRequest.Value);
var rayCastHit = Physics.RaycastNonAlloc(ray, k_CachedHit, k_MouseInputRaycastDistance, k_MouseQueryLayerMask) > 0;
if (rayCastHit && GetTargetObject(ref k_CachedHit[0]) != 0)
{
//these two actions will queue one after the other, causing us to run over to our target and take a swing.
var chase_data = new ActionRequestData();
chase_data.ActionTypeEnum = ActionType.GENERAL_CHASE;
chase_data.Amount = ActionData.ActionDescriptions[ActionType.TANK_BASEATTACK][0].Range;
chase_data.TargetIds = new ulong[] {GetTargetObject(ref k_CachedHit[0])};
m_NetworkCharacter.ClientSendActionRequest(ref chase_data);
var hit_data = new ActionRequestData();
hit_data.ShouldQueue = true; //wait your turn--don't clobber the chase action.
hit_data.ActionTypeEnum = ActionType.TANK_BASEATTACK;
m_NetworkCharacter.ClientSendActionRequest(ref hit_data);
}
else
{
var data = new ActionRequestData();
data.ActionTypeEnum = ActionType.TANK_BASEATTACK;
m_NetworkCharacter.ClientSendActionRequest(ref data);
}
m_AttackClickRequest = null;
}
}
private void Update()
{
//we do this in "Update" rather than "FixedUpdate" because discrete clicks can be missed in FixedUpdate.
if (Input.GetMouseButtonDown(1))
{
m_AttackClickRequest = Input.mousePosition;
}
}
/// <summary>
/// Gets the Target NetworkId from the Raycast hit, or 0 if Raycast didn't contact a Networked Object.
/// </summary>
private ulong GetTargetObject(ref RaycastHit hit)
{
if (hit.collider == null)
{
return 0;
}
var targetObj = hit.collider.GetComponent<NetworkedObject>();
if (targetObj == null)
{
return 0;
}
return targetObj.NetworkId;
}
}
}