working on charging system

This commit is contained in:
2025-07-30 08:26:07 +02:00
parent 61a42bdf10
commit 407454555f
13 changed files with 544 additions and 497 deletions

View File

@@ -19,7 +19,6 @@ namespace RebootReality.jelycho.Player.HUD {
public VisualElement Root;
public VisualElement Slot;
public VisualElement Icon;
public Label QuantityLabel;
}
readonly List<InventorySlot> m_InventorySlots = new List<InventorySlot>();
@@ -43,13 +42,11 @@ namespace RebootReality.jelycho.Player.HUD {
InventorySlot slot = new InventorySlot {
Root = slotRoot,
Slot = slotRoot.Q<VisualElement>("player-hud__inventory-slot"),
Icon = slotRoot.Q<VisualElement>("player-hud__inventory-slot-icon"),
QuantityLabel = slotRoot.Q<Label>("player-hud__inventory-slot-quantity"),
Icon = slotRoot.Q<VisualElement>("player-hud__inventory-slot-icon")
};
Assert.IsNotNull(slot.Root, "Slot root cannot be null");
Assert.IsNotNull(slot.Slot, "Slot element cannot be null");
Assert.IsNotNull(slot.Icon, "Slot icon cannot be null");
Assert.IsNotNull(slot.QuantityLabel, "Slot quantity label cannot be null");
m_InventorySlots.Add(slot);
OnSlotUpdated(i);
@@ -86,25 +83,15 @@ namespace RebootReality.jelycho.Player.HUD {
s_Logger.Error($"Invalid slot index: {slotIndex}. Inventory has {m_InventorySlots.Count} slots.");
return;
}
int quantity = m_Inventory.GetQuantity(slotIndex);
if (quantity <= 0) {
m_InventorySlots[slotIndex].QuantityLabel.style.display = DisplayStyle.None;
ItemActor itemActor = m_Inventory.GetItem(slotIndex);
if (itemActor == null) {
m_InventorySlots[slotIndex].Icon.style.backgroundImage = null;
m_InventorySlots[slotIndex].Icon.style.display = DisplayStyle.None;
} else {
m_InventorySlots[slotIndex].QuantityLabel.style.display = DisplayStyle.Flex;
m_InventorySlots[slotIndex].QuantityLabel.text = quantity.ToString();
ItemActor itemActor = m_Inventory.GetFirstItem(slotIndex);
if (itemActor != null) {
m_InventorySlots[slotIndex].Icon.style.backgroundImage = new StyleBackground(itemActor.Config.icon);
m_InventorySlots[slotIndex].Icon.style.display = DisplayStyle.Flex;
} else {
m_InventorySlots[slotIndex].Icon.style.backgroundImage = null;
m_InventorySlots[slotIndex].Icon.style.display = DisplayStyle.None;
}
m_InventorySlots[slotIndex].Icon.style.backgroundImage = new StyleBackground(itemActor.Config.icon);
m_InventorySlots[slotIndex].Icon.style.display = DisplayStyle.Flex;
}
}
}

View File

@@ -1,4 +1,5 @@
using R3;
using System;
using R3;
using RebootKit.Engine.Extensions;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
@@ -11,6 +12,16 @@ using UnityEngine;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootReality.jelycho.Player {
[Serializable]
struct PlayerItemTypeHandsAnimationsConfig {
public ItemType itemType;
// @TODO: Cache hashes for state names
public string idle;
public string charging;
public string chargedUse;
}
public class PlayerActor : Actor {
static readonly Logger s_Logger = new Logger(nameof(PlayerActor));
@@ -48,6 +59,13 @@ namespace RebootReality.jelycho.Player {
float m_CharacterTurnVelocity = 0.0f;
[Header("Animations")]
[SerializeField] int m_HandsLayerIndex;
[SerializeField] PlayerItemTypeHandsAnimationsConfig[] m_ItemTypeHandsAnimations;
[SerializeField] PlayerItemTypeHandsAnimationsConfig m_DefaultItemHandsAnimations;
[SerializeField] string m_HandsIdleStateName = "Hands Locomotion";
[Header("Dragging")]
[SerializeField] Transform m_DragGutStartPosition;
[SerializeField] PhysicsObjectDragger m_PhysicsDragger;
@@ -73,10 +91,12 @@ namespace RebootReality.jelycho.Player {
[SerializeField] int m_InventorySize = 10;
public Inventory Inventory { get; private set; }
public ReactiveProperty<int> SelectedInventorySlot { get; private set; } = new ReactiveProperty<int>(0);
ItemActor m_EquippedItem;
public ReactiveProperty<int> SelectedInventorySlot { get; private set; } = new ReactiveProperty<int>(0);
bool m_IsCharging;
float m_ChargeTimer;
public float3 LookDirection {
get {
@@ -120,142 +140,6 @@ namespace RebootReality.jelycho.Player {
Inventory.OnItemDropped -= OnItemDropped;
}
//
// @MARK: Actor
//
public override void OnServerTick(float deltaTime) {
base.OnServerTick(deltaTime);
NativeArray<byte> remoteStateData = DataSerializationUtils.Serialize(m_RemoteState);
SendActorEvent((byte) PlayerActorEvents.UpdatedRemoteState, remoteStateData);
}
public override void OnClientTick(float deltaTime) {
base.OnClientTick(deltaTime);
if (m_IsSetupAsOwner) {
TickCamera();
UpdateAnimator(m_Locomotion.Velocity);
SenseInteractable();
m_SyncRemoteStateTimer -= deltaTime;
if (m_SyncRemoteStateTimer <= 0.0f) {
m_SyncRemoteStateTimer = 1.0f / NetworkSystem.TickRate.IndexValue;
RemotePlayerActorState remoteState = new RemotePlayerActorState {
Position = transform.position,
Velocity = m_Locomotion.Velocity,
LookPitch = m_Camera.Pitch,
LookYaw = m_Camera.Yaw,
IsGrounded = m_Locomotion.IsGrounded
};
NativeArray<byte> data = DataSerializationUtils.Serialize(remoteState);
SendActorCommand((byte) PlayerActorCommands.UpdateRemoteState, data);
}
} else {
InterpolateActorState(deltaTime);
}
TickCharacterRotation();
}
protected override void OnActorCommandServer(ulong senderID, ActorCommand actorCommand) {
switch ((PlayerActorCommands)actorCommand.CommandID) {
case PlayerActorCommands.UpdateRemoteState: {
RemotePlayerActorState remoteState = new RemotePlayerActorState();
DataSerializationUtils.Deserialize(actorCommand.Data, ref remoteState);
m_RemoteState = remoteState;
break;
}
case PlayerActorCommands.PickupItem: {
PlayerActorPickupItemCommand command = new PlayerActorPickupItemCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
Actor itemActor = RR.FindSpawnedActor(command.ItemActorID);
if (itemActor is ItemActor item) {
if (math.distancesq(itemActor.transform.position, m_HeadBoneTransform.position) <=
m_ItemPickupDistance * m_ItemPickupDistance) {
if (Inventory.TryPickup(item)) {
s_Logger.Info($"Item {item.name} picked up successfully by player {name}.");
UpdateEquippedItem();
} else {
s_Logger.Info($"Failed to pick up item {item.name}. Inventory is full.");
}
} else {
s_Logger.Info($"Item actor {item.name} is too far away to pick up.");
}
} else {
s_Logger.Error($"Item actor with ID {command.ItemActorID} not found.");
}
break;
}
case PlayerActorCommands.SelectItemSlot: {
PlayerActorSelectItemSlotCommand command = new PlayerActorSelectItemSlotCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
if (command.SlotIndex < 0 || command.SlotIndex >= Inventory.SlotsCount) {
s_Logger.Error($"Invalid slot index {command.SlotIndex}. Must be between 0 and {Inventory.SlotsCount - 1}.");
return;
}
SelectedInventorySlot.Value = command.SlotIndex;
UpdateEquippedItem();
break;
}
case PlayerActorCommands.DropItem: {
PlayerActorDropItemCommand command = new PlayerActorDropItemCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
if (command.Count == 1) {
Inventory.TryDropOne(command.InventorySlotIndex);
} else {
s_Logger.Error("DropItem command with count != 1 is not supported yet.");
}
break;
}
}
}
protected override void OnActorEventClient(ActorEvent actorEvent) {
base.OnActorEventClient(actorEvent);
switch ((PlayerActorEvents)actorEvent.EventID) {
case PlayerActorEvents.UpdatedRemoteState: {
RemotePlayerActorState remoteState = new RemotePlayerActorState();
DataSerializationUtils.Deserialize(actorEvent.Data, ref remoteState);
m_RemoteState = remoteState;
break;
}
case PlayerActorEvents.PrimaryEquippedItemChanged: {
PlayerActorPrimaryEquippedItemChangedEvent itemChangedEvent =
new PlayerActorPrimaryEquippedItemChangedEvent();
DataSerializationUtils.Deserialize(actorEvent.Data, ref itemChangedEvent);
if (itemChangedEvent.ItemActorID == 0) {
m_EquippedItem = null;
} else {
Actor itemActor = RR.FindSpawnedActor(itemChangedEvent.ItemActorID);
if (itemActor is ItemActor item) {
s_Logger.Info($"Primary equipped item changed to {item.name} for player {name}.");
m_EquippedItem = item;
} else {
s_Logger.Error($"Primary equipped item with ID {itemChangedEvent.ItemActorID} not found.");
}
}
break;
}
default:
s_Logger.Error("Invalid actor event received: " + actorEvent.EventID);
break;
}
}
//
// @MARK: Controller API
//
@@ -314,7 +198,7 @@ namespace RebootReality.jelycho.Player {
m_PhysicsDragger.Drop();
}
public void DropItem() {
if (!m_IsSetupAsOwner) {
s_Logger.Error("Cannot drop item when not set up as owner.");
@@ -323,30 +207,98 @@ namespace RebootReality.jelycho.Player {
if (m_EquippedItem != null) {
PlayerActorDropItemCommand command = new PlayerActorDropItemCommand {
InventorySlotIndex = SelectedInventorySlot.Value,
Count = 1
InventorySlotIndex = (byte) SelectedInventorySlot.Value
};
SendActorCommand((byte) PlayerActorCommands.DropItem, ref command);
}
}
public void PrimaryAction() {
PlayerItemTypeHandsAnimationsConfig GetHandsAnimationsConfig(ItemType itemType) {
foreach (PlayerItemTypeHandsAnimationsConfig config in m_ItemTypeHandsAnimations) {
if (config.itemType == itemType) {
return config;
}
}
return m_DefaultItemHandsAnimations;
}
void SetHandsIdleAnimation() {
if (m_EquippedItem != null) {
PlayerItemTypeHandsAnimationsConfig animationsConfig =
GetHandsAnimationsConfig(m_EquippedItem.Config.itemType);
m_Animator.CrossFade(animationsConfig.idle, 0.0f, m_HandsLayerIndex);
} else {
m_Animator.CrossFade(m_HandsIdleStateName, 0.0f, m_HandsLayerIndex);
}
}
void SetChargingAnimation() {
if (m_EquippedItem != null) {
PlayerItemTypeHandsAnimationsConfig animationsConfig =
GetHandsAnimationsConfig(m_EquippedItem.Config.itemType);
m_Animator.CrossFade(animationsConfig.charging, 0.0f, m_HandsLayerIndex);
}
}
void SetChargedUseAnimation() {
if (m_EquippedItem != null) {
PlayerItemTypeHandsAnimationsConfig animationsConfig =
GetHandsAnimationsConfig(m_EquippedItem.Config.itemType);
m_Animator.CrossFade(animationsConfig.chargedUse, 0.0f, m_HandsLayerIndex);
}
}
public void BeginPrimaryAction() {
if (!m_IsSetupAsOwner) {
s_Logger.Error("Cannot perform primary action when not set up as owner.");
s_Logger.Error("Cannot begin primary action when not set up as owner.");
return;
}
// if (!IsOwner()) {
// s_Logger.Error("Only the owner can perform primary actions.");
// return;
// }
// if (TryGetBeaconPosition(out Vector3 beaconPosition)) {
// SetAnimatorTriggerRpc(AnimatorParamHashes.Throw);
//
// if (RR.World.Context is WorldContext worldContext) {
// worldContext.BaseManager.TrySpawnBeacon(beaconPosition);
// }
// }
if (m_EquippedItem == null) {
return;
}
if (m_EquippedItem.Config.chargeAction != null && m_EquippedItem.Config.isChargeable) {
if (m_EquippedItem.Config.chargeAction.OnChargeStart(this, m_EquippedItem)) {
m_IsCharging = true;
m_ChargeTimer = 0.0f;
SetChargingAnimation();
}
}
}
float GetChargeProgress() {
if (m_EquippedItem == null || !m_EquippedItem.Config.isChargeable) {
return 0.0f;
}
float chargeProgress = Mathf.InverseLerp(m_EquippedItem.Config.minChargeDuration,
m_EquippedItem.Config.maxChargeDuration,
m_ChargeTimer);
return Mathf.Clamp01(chargeProgress);
}
public void EndPrimaryAction() {
if (!m_IsSetupAsOwner) {
s_Logger.Error("Cannot end primary action when not set up as owner.");
return;
}
if (m_IsCharging) {
ItemConfig itemConfig = m_EquippedItem.Config;
if (m_ChargeTimer >= itemConfig.minChargeDuration) {
float chargeProgress = GetChargeProgress();
itemConfig.chargeAction.OnChargeEnd(this, m_EquippedItem, chargeProgress);
SetChargedUseAnimation();
} else {
itemConfig.chargeAction.OnChargeCancel(this, m_EquippedItem);
SetHandsIdleAnimation();
}
m_IsCharging = false;
}
}
public void SecondaryAction() {
@@ -355,7 +307,13 @@ namespace RebootReality.jelycho.Player {
return;
}
m_Animator.SetTrigger(AnimatorParamHashes.Block);
if (m_IsCharging) {
m_EquippedItem.Config.chargeAction.OnChargeCancel(this, m_EquippedItem);
m_IsCharging = false;
m_ChargeTimer = 0.0f;
SetHandsIdleAnimation();
}
}
public void Interact() {
@@ -372,6 +330,171 @@ namespace RebootReality.jelycho.Player {
}
}
//
// @MARK: Actor
//
public override void OnServerTick(float deltaTime) {
base.OnServerTick(deltaTime);
NativeArray<byte> remoteStateData = DataSerializationUtils.Serialize(m_RemoteState);
SendActorEvent((byte) PlayerActorEvents.UpdatedRemoteState, remoteStateData);
}
public override void OnClientTick(float deltaTime) {
base.OnClientTick(deltaTime);
if (m_IsSetupAsOwner) {
TickCamera();
UpdateAnimator(m_Locomotion.Velocity);
SenseInteractable();
if (m_IsCharging) {
m_ChargeTimer += deltaTime;
m_EquippedItem.Config.chargeAction.OnChargeUpdate(this, m_EquippedItem, GetChargeProgress());
}
m_SyncRemoteStateTimer -= deltaTime;
if (m_SyncRemoteStateTimer <= 0.0f) {
m_SyncRemoteStateTimer = 1.0f / NetworkSystem.TickRate.IndexValue;
RemotePlayerActorState remoteState = new RemotePlayerActorState {
Position = transform.position,
Velocity = m_Locomotion.Velocity,
LookPitch = m_Camera.Pitch,
LookYaw = m_Camera.Yaw,
IsGrounded = m_Locomotion.IsGrounded
};
NativeArray<byte> data = DataSerializationUtils.Serialize(remoteState);
SendActorCommand((byte) PlayerActorCommands.UpdateRemoteState, data);
}
} else {
InterpolateActorState(deltaTime);
}
TickCharacterRotation();
}
protected override void OnActorCommandServer(ulong senderID, ActorCommand actorCommand) {
switch ((PlayerActorCommands) actorCommand.CommandID) {
case PlayerActorCommands.UpdateRemoteState: {
RemotePlayerActorState remoteState = new RemotePlayerActorState();
DataSerializationUtils.Deserialize(actorCommand.Data, ref remoteState);
m_RemoteState = remoteState;
break;
}
case PlayerActorCommands.PickupItem: {
PlayerActorPickupItemCommand command = new PlayerActorPickupItemCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
Actor itemActor = RR.FindSpawnedActor(command.ItemActorID);
if (itemActor is ItemActor item) {
if (math.distancesq(itemActor.transform.position, m_HeadBoneTransform.position) <=
m_ItemPickupDistance * m_ItemPickupDistance) {
if (Inventory.TryPickup(item)) {
s_Logger.Info($"Item {item.name} picked up successfully by player {name}.");
UpdateEquippedItem();
} else {
s_Logger.Info($"Failed to pick up item {item.name}. Inventory is full.");
}
} else {
s_Logger.Info($"Item actor {item.name} is too far away to pick up.");
}
} else {
s_Logger.Error($"Item actor with ID {command.ItemActorID} not found.");
}
break;
}
case PlayerActorCommands.SelectItemSlot: {
PlayerActorSelectItemSlotCommand command = new PlayerActorSelectItemSlotCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
if (command.SlotIndex < 0 || command.SlotIndex >= Inventory.SlotsCount) {
s_Logger.Error($"Invalid slot index {command.SlotIndex}. Must be between 0 and {Inventory.SlotsCount - 1}.");
return;
}
SelectedInventorySlot.Value = command.SlotIndex;
UpdateEquippedItem();
break;
}
case PlayerActorCommands.DropItem: {
PlayerActorDropItemCommand command = new PlayerActorDropItemCommand();
DataSerializationUtils.Deserialize(actorCommand.Data, ref command);
Inventory.TryDrop(command.InventorySlotIndex, out _);
break;
}
}
}
protected override void OnActorEventClient(ActorEvent actorEvent) {
base.OnActorEventClient(actorEvent);
switch ((PlayerActorEvents) actorEvent.EventID) {
case PlayerActorEvents.UpdatedRemoteState: {
RemotePlayerActorState remoteState = new RemotePlayerActorState();
DataSerializationUtils.Deserialize(actorEvent.Data, ref remoteState);
m_RemoteState = remoteState;
break;
}
case PlayerActorEvents.PrimaryEquippedItemChanged: {
PlayerActorPrimaryEquippedItemChangedEvent itemChangedEvent =
new PlayerActorPrimaryEquippedItemChangedEvent();
DataSerializationUtils.Deserialize(actorEvent.Data, ref itemChangedEvent);
if (itemChangedEvent.ItemActorID == 0) {
m_EquippedItem = null;
} else {
Actor itemActor = RR.FindSpawnedActor(itemChangedEvent.ItemActorID);
if (itemActor is ItemActor item) {
s_Logger.Info($"Primary equipped item changed to {item.name} for player {name}.");
m_EquippedItem = item;
} else {
s_Logger.Error($"Primary equipped item with ID {itemChangedEvent.ItemActorID} not found.");
}
}
SetHandsIdleAnimation();
break;
}
case PlayerActorEvents.UpdateInventory: {
if (RR.IsServer()) {
break;
}
PlayerUpdateInventoryEvent updateInventoryEvent = new PlayerUpdateInventoryEvent();
DataSerializationUtils.Deserialize(actorEvent.Data, ref updateInventoryEvent);
for (int i = 0; i < Inventory.SlotsCount; i++) {
ushort actorID = updateInventoryEvent.SlotsActorIDs[i];
if (actorID == 0) {
Inventory.SetItem(i, null);
} else {
Actor itemActor = RR.FindSpawnedActor(actorID);
if (itemActor is ItemActor item) {
Inventory.SetItem(i, item);
} else {
s_Logger.Error($"Item actor with ID {actorID} not found for inventory slot {i}.");
}
}
}
break;
}
default:
s_Logger.Error("Invalid actor event received: " + actorEvent.EventID);
break;
}
}
//
// @MARK: Owner
//
@@ -456,13 +579,28 @@ namespace RebootReality.jelycho.Player {
//
// @MARK: Server
//
void SendInventoryState() {
if (!RR.IsServer()) {
s_Logger.Error("Only the server can send inventory state.");
return;
}
PlayerUpdateInventoryEvent updateInventoryEvent = new PlayerUpdateInventoryEvent();
updateInventoryEvent.SlotsActorIDs = new NativeArray<ushort>(Inventory.SlotsCount, Allocator.Temp);
for (int i = 0; i < Inventory.SlotsCount; i++) {
updateInventoryEvent.SlotsActorIDs[i] = Inventory.GetItem(i)?.ActorID ?? (ushort) 0;
}
SendActorEvent((byte) PlayerActorEvents.UpdateInventory, ref updateInventoryEvent);
}
void OnItemDropped(ItemActor item) {
if (!RR.IsServer()) {
return;
}
// @BUG: Sometimes the item will not update it's physics state and will keep floating in the air. It's rare?
UpdateEquippedItem();
item.SetHidden(false);
@@ -470,6 +608,8 @@ namespace RebootReality.jelycho.Player {
item.transform.position = m_HeadBoneTransform.position + m_HeadBoneTransform.forward * 1.0f;
item.transform.rotation = Quaternion.LookRotation(m_HeadBoneTransform.forward, Vector3.up);
SendInventoryState();
}
void OnItemPickedUp(ItemActor item) {
@@ -478,6 +618,7 @@ namespace RebootReality.jelycho.Player {
}
item.SetHidden(true);
SendInventoryState();
}
void UpdateEquippedItem() {
@@ -486,7 +627,7 @@ namespace RebootReality.jelycho.Player {
return;
}
ItemActor itemActor = Inventory.GetFirstItem(SelectedInventorySlot.Value);
ItemActor itemActor = Inventory.GetItem(SelectedInventorySlot.Value);
if (itemActor == m_EquippedItem) {
return;
}
@@ -504,9 +645,11 @@ namespace RebootReality.jelycho.Player {
PlayerActorPrimaryEquippedItemChangedEvent itemChangedEvent =
new PlayerActorPrimaryEquippedItemChangedEvent {
ItemActorID = m_EquippedItem != null ? m_EquippedItem.ActorID : (ushort)0
ItemActorID = m_EquippedItem != null ? m_EquippedItem.ActorID : (ushort) 0
};
SendActorEvent((byte) PlayerActorEvents.PrimaryEquippedItemChanged, ref itemChangedEvent);
SetHandsIdleAnimation();
}
public void WarpTo(Vector3 position) {
@@ -682,12 +825,12 @@ namespace RebootReality.jelycho.Player {
m_Animator.SetInteger(AnimatorParamHashes.Holding, 1);
}
}
public class PlayerActorData : IActorData {
public void Serialize(NetworkBufferWriter writer) { }
public void Deserialize(NetworkBufferReader reader) { }
public int GetMaxBytes() {
return 0;
}
@@ -720,7 +863,7 @@ namespace RebootReality.jelycho.Player {
return sizeof(float) * 3 + // Position;
sizeof(float) * 3 + // Velocity
sizeof(float) * 2 + // LookPitch, LookYaw
sizeof(bool); // IsGrounded
sizeof(bool); // IsGrounded
}
}
@@ -751,21 +894,18 @@ namespace RebootReality.jelycho.Player {
}
struct PlayerActorDropItemCommand : IActorData {
public int InventorySlotIndex;
public int Count;
public byte InventorySlotIndex;
public void Serialize(NetworkBufferWriter writer) {
writer.Write(InventorySlotIndex);
writer.Write(Count);
}
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out InventorySlotIndex);
reader.Read(out Count);
}
public int GetMaxBytes() {
return sizeof(int) * 2; // InventorySlotIndex, Count
return sizeof(byte);
}
}
@@ -795,7 +935,7 @@ namespace RebootReality.jelycho.Player {
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out SlotIndex);
}
public int GetMaxBytes() {
return sizeof(int); // SlotIndex
}
@@ -806,6 +946,7 @@ namespace RebootReality.jelycho.Player {
None = 0x00,
PrimaryEquippedItemChanged = 0x01,
UpdatedRemoteState = 0x02,
UpdateInventory = 0x03,
}
struct PlayerActorPrimaryEquippedItemChangedEvent : IActorData {
@@ -823,4 +964,28 @@ namespace RebootReality.jelycho.Player {
return sizeof(ushort); // ItemActorID
}
}
struct PlayerUpdateInventoryEvent : IActorData {
public NativeArray<ushort> SlotsActorIDs;
public void Serialize(NetworkBufferWriter writer) {
writer.Write((byte) SlotsActorIDs.Length);
for (int i = 0; i < SlotsActorIDs.Length; i++) {
writer.Write(SlotsActorIDs[i]);
}
}
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out byte slotsCount);
SlotsActorIDs = new NativeArray<ushort>(slotsCount, Allocator.Temp);
for (int i = 0; i < slotsCount; i++) {
reader.Read(out ushort actorID);
SlotsActorIDs[i] = actorID;
}
}
public int GetMaxBytes() {
return sizeof(byte) + SlotsActorIDs.Length * sizeof(ushort);
}
}
}

View File

@@ -128,8 +128,12 @@ namespace RebootReality.jelycho.Player {
m_Actor.DropItem();
}
if (m_Config.primaryActionReference.action.WasPressedThisFrame()) {
m_Actor.BeginPrimaryAction();
}
if (m_Config.primaryActionReference.action.WasReleasedThisFrame()) {
m_Actor.PrimaryAction();
m_Actor.EndPrimaryAction();
}
if (m_Config.secondaryActionReference.action.WasReleasedThisFrame()) {