working on inventory
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Services.Simulation.Sensors;
|
||||
using RebootKit.Engine.Simulation.Sensors;
|
||||
using RebootReality.jelycho.Main;
|
||||
using Unity.Cinemachine;
|
||||
|
||||
41
Assets/jelycho/Code/Player/GenericSensor.cs
Normal file
41
Assets/jelycho/Code/Player/GenericSensor.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using RebootKit.Engine.Simulation.Sensors;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Player {
|
||||
[Serializable]
|
||||
public struct GenericSensor : ISensor {
|
||||
[SerializeField] Transform m_Origin;
|
||||
[SerializeField] LayerMask m_LayerMask;
|
||||
[SerializeField] float m_MaxDistance;
|
||||
|
||||
public GameObject Sense() {
|
||||
Ray ray = new Ray(m_Origin.position, m_Origin.forward);
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, m_MaxDistance, m_LayerMask)) {
|
||||
return hit.collider.gameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GenericSensor<T> : ISensor<T> where T : class {
|
||||
[SerializeField] Transform m_Origin;
|
||||
[SerializeField] LayerMask m_LayerMask;
|
||||
[SerializeField] float m_MaxDistance;
|
||||
|
||||
public T Sense() {
|
||||
Ray ray = new Ray(m_Origin.position, m_Origin.forward);
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, m_MaxDistance, m_LayerMask)) {
|
||||
if (hit.collider.TryGetComponent(out T component)) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/GenericSensor.cs.meta
Normal file
3
Assets/jelycho/Code/Player/GenericSensor.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 584138e7a2834fbaa3f29d381ee6d0f5
|
||||
timeCreated: 1752181704
|
||||
3
Assets/jelycho/Code/Player/HUD.meta
Normal file
3
Assets/jelycho/Code/Player/HUD.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 433c5938a11248afa61f1ced56bd407d
|
||||
timeCreated: 1752067932
|
||||
83
Assets/jelycho/Code/Player/HUD/ObjectsLabelsVC.cs
Normal file
83
Assets/jelycho/Code/Player/HUD/ObjectsLabelsVC.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RebootKit.Engine.Main;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RebootReality.jelycho.Player.HUD {
|
||||
public class ObjectsLabelsVC : MonoBehaviour {
|
||||
[SerializeField] UIDocument m_Document;
|
||||
[SerializeField] VisualTreeAsset m_LabelTemplate;
|
||||
|
||||
IObjectPool<LabelEntry> m_LabelPool;
|
||||
|
||||
readonly List<LabelEntry> m_ActiveLabels = new List<LabelEntry>();
|
||||
|
||||
void Awake() {
|
||||
m_LabelPool = new ObjectPool<LabelEntry>(() => {
|
||||
LabelEntry entry = new LabelEntry();
|
||||
entry.Parent = this;
|
||||
|
||||
entry.Root = new VisualElement();
|
||||
entry.Root.style.position = Position.Absolute;
|
||||
entry.Root.style.visibility = Visibility.Hidden;
|
||||
entry.Root.style.minWidth = 300;
|
||||
entry.Root.style.height = 64;
|
||||
|
||||
m_LabelTemplate.CloneTree(entry.Root);
|
||||
entry.Label = entry.Root.Q<Label>("player-hud__object-label");
|
||||
|
||||
m_Document.rootVisualElement.Add(entry.Root);
|
||||
return entry;
|
||||
},
|
||||
entry => {
|
||||
entry.Root.style.visibility = Visibility.Visible;
|
||||
m_ActiveLabels.Add(entry);
|
||||
},
|
||||
entry => {
|
||||
entry.Root.style.visibility = Visibility.Hidden;
|
||||
m_ActiveLabels.RemoveSwapBack(entry);
|
||||
},
|
||||
entry => {
|
||||
m_ActiveLabels.RemoveSwapBack(entry);
|
||||
m_Document.rootVisualElement.Remove(entry.Root);
|
||||
});
|
||||
}
|
||||
|
||||
void Update() {
|
||||
foreach (LabelEntry entry in m_ActiveLabels) {
|
||||
Vector3 worldPosition = entry.TargetTransform.position;
|
||||
Vector2 screenPosition = RR.MainCamera.WorldToScreenPoint(worldPosition);
|
||||
|
||||
entry.Root.style.left = screenPosition.x - entry.Root.contentRect.width * 0.5f;
|
||||
entry.Root.style.top = Screen.height - screenPosition.y - entry.Root.contentRect.height * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable CreateLabel(Transform targetTransform, string labelText) {
|
||||
if (targetTransform == null) {
|
||||
throw new ArgumentNullException(nameof(targetTransform));
|
||||
}
|
||||
|
||||
LabelEntry entry = m_LabelPool.Get();
|
||||
entry.TargetTransform = targetTransform;
|
||||
entry.Label.text = labelText;
|
||||
entry.Root.style.visibility = Visibility.Visible;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
class LabelEntry : IDisposable {
|
||||
public ObjectsLabelsVC Parent;
|
||||
public VisualElement Root;
|
||||
public Label Label;
|
||||
public Transform TargetTransform;
|
||||
|
||||
public void Dispose() {
|
||||
Parent.m_LabelPool.Release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/HUD/ObjectsLabelsVC.cs.meta
Normal file
3
Assets/jelycho/Code/Player/HUD/ObjectsLabelsVC.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42a4ccf83ed64ac58aef118275bd2414
|
||||
timeCreated: 1752068186
|
||||
45
Assets/jelycho/Code/Player/HUD/PlayerHUD.cs
Normal file
45
Assets/jelycho/Code/Player/HUD/PlayerHUD.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using R3;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho.Player.HUD {
|
||||
public class PlayerHUD : MonoBehaviour {
|
||||
static readonly Logger s_Logger = new Logger(nameof(PlayerHUD));
|
||||
|
||||
[SerializeField] UIDocument m_MainDocument;
|
||||
|
||||
[field: SerializeField] public ObjectsLabelsVC ObjectsLabels { get; private set; }
|
||||
[field: SerializeField] public PlayerInventoryUI InventoryUI { get; private set; }
|
||||
|
||||
PlayerActor m_PlayerActor;
|
||||
DisposableBag m_ActorBag;
|
||||
|
||||
void OnEnable() {
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
InventoryUI.CleanUp();
|
||||
m_ActorBag.Dispose();
|
||||
}
|
||||
|
||||
public void SetPlayerActor(PlayerActor actor) {
|
||||
m_ActorBag.Dispose();
|
||||
|
||||
if (actor == null) {
|
||||
m_PlayerActor = null;
|
||||
InventoryUI.CleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
m_ActorBag = new DisposableBag();
|
||||
m_PlayerActor = actor;
|
||||
InventoryUI.Configure(m_MainDocument.rootVisualElement.Q("player-hud__inventory-slots"), actor.Inventory);
|
||||
|
||||
actor.SelectedInventorySlot.Subscribe(x => {
|
||||
InventoryUI.SetSelectedSlot(x);
|
||||
}).AddTo(ref m_ActorBag);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/HUD/PlayerHUD.cs.meta
Normal file
3
Assets/jelycho/Code/Player/HUD/PlayerHUD.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0712c9d21534362b219edf44601f57d
|
||||
timeCreated: 1752067887
|
||||
111
Assets/jelycho/Code/Player/HUD/PlayerInventoryUI.cs
Normal file
111
Assets/jelycho/Code/Player/HUD/PlayerInventoryUI.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RebootReality.jelycho.Items;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.UIElements;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho.Player.HUD {
|
||||
public class PlayerInventoryUI : MonoBehaviour {
|
||||
static readonly Logger s_Logger = new Logger(nameof(PlayerInventoryUI));
|
||||
|
||||
[SerializeField] VisualTreeAsset m_InventorySlotTemplate;
|
||||
|
||||
Inventory m_Inventory;
|
||||
VisualElement m_Root;
|
||||
|
||||
struct InventorySlot {
|
||||
public VisualElement Root;
|
||||
public VisualElement Slot;
|
||||
public VisualElement Icon;
|
||||
public Label QuantityLabel;
|
||||
}
|
||||
|
||||
readonly List<InventorySlot> m_InventorySlots = new List<InventorySlot>();
|
||||
|
||||
public void Configure(VisualElement root, Inventory inventory) {
|
||||
CleanUp();
|
||||
|
||||
m_Root = root;
|
||||
m_Inventory = inventory;
|
||||
m_Inventory.OnSlotUpdated += OnSlotUpdated;
|
||||
|
||||
m_Root.Clear();
|
||||
|
||||
for (int i = 0; i < m_Inventory.SlotsCount; i++) {
|
||||
VisualElement slotRoot = m_InventorySlotTemplate.CloneTree();
|
||||
m_Root.Add(slotRoot);
|
||||
|
||||
slotRoot .style.width = new StyleLength(new Length(72, LengthUnit.Pixel));
|
||||
slotRoot.style.height = new StyleLength(new Length(72, LengthUnit.Pixel));
|
||||
|
||||
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"),
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanUp() {
|
||||
if (m_Root != null) {
|
||||
m_Root.Clear();
|
||||
m_Root = null;
|
||||
}
|
||||
|
||||
if (m_Inventory != null) {
|
||||
m_Inventory.OnSlotUpdated -= OnSlotUpdated;
|
||||
m_Inventory = null;
|
||||
}
|
||||
|
||||
m_InventorySlots.Clear();
|
||||
}
|
||||
|
||||
public void SetSelectedSlot(int slotIndex) {
|
||||
if (slotIndex < 0 || slotIndex >= m_InventorySlots.Count) {
|
||||
s_Logger.Error($"Invalid slot index: {slotIndex}. Inventory has {m_InventorySlots.Count} slots.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_InventorySlots.Count; i++) {
|
||||
m_InventorySlots[i].Slot.EnableInClassList("inventory-slot-selected", i == slotIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void OnSlotUpdated(int slotIndex) {
|
||||
if (slotIndex < 0 || slotIndex >= m_InventorySlots.Count) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Player/HUD/PlayerInventoryUI.cs.meta
Normal file
3
Assets/jelycho/Code/Player/HUD/PlayerInventoryUI.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffb52c4d0a3c4e17917100cbb52494ea
|
||||
timeCreated: 1752183541
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using R3;
|
||||
using RebootKit.Engine.Extensions;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Services.Simulation;
|
||||
using RebootKit.Engine.Simulation.Sensors;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootReality.jelycho.Items;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
@@ -26,42 +27,6 @@ namespace RebootReality.jelycho.Player {
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GenericSensor : ISensor {
|
||||
[SerializeField] Transform m_Origin;
|
||||
[SerializeField] LayerMask m_LayerMask;
|
||||
[SerializeField] float m_MaxDistance;
|
||||
|
||||
public GameObject Sense() {
|
||||
Ray ray = new Ray(m_Origin.position, m_Origin.forward);
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, m_MaxDistance, m_LayerMask)) {
|
||||
return hit.collider.gameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GenericSensor<T> : ISensor<T> where T : class {
|
||||
[SerializeField] Transform m_Origin;
|
||||
[SerializeField] LayerMask m_LayerMask;
|
||||
[SerializeField] float m_MaxDistance;
|
||||
|
||||
public T Sense() {
|
||||
Ray ray = new Ray(m_Origin.position, m_Origin.forward);
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, m_MaxDistance, m_LayerMask)) {
|
||||
if (hit.collider.TryGetComponent(out T component)) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerActor : NetworkBehaviour {
|
||||
static readonly Logger s_Logger = new Logger(nameof(PlayerActor));
|
||||
|
||||
@@ -115,6 +80,15 @@ namespace RebootReality.jelycho.Player {
|
||||
[Header("Network")]
|
||||
[SerializeField] float m_MinTeleportDistance = 0.5f;
|
||||
|
||||
PlayerActorState m_NetworkState;
|
||||
|
||||
[Header("Inventory")]
|
||||
[SerializeField] int m_InventorySize = 10;
|
||||
|
||||
public Inventory Inventory { get; private set; }
|
||||
|
||||
public ReactiveProperty<int> SelectedInventorySlot { get; private set; } = new ReactiveProperty<int>(0);
|
||||
|
||||
public float3 LookDirection {
|
||||
get {
|
||||
float pitchRad = math.radians(-m_Camera.Pitch);
|
||||
@@ -125,60 +99,21 @@ namespace RebootReality.jelycho.Player {
|
||||
}
|
||||
}
|
||||
|
||||
readonly ReactiveProperty<IInteractable> m_TargetInteractable = new ReactiveProperty<IInteractable>(null);
|
||||
public ReadOnlyReactiveProperty<IInteractable> TargetInteractable {
|
||||
get {
|
||||
return m_TargetInteractable;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
Inventory = new Inventory(m_InventorySize);
|
||||
}
|
||||
|
||||
void Start() {
|
||||
m_CameraSpring.Initialize();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn() {
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsOwner) {
|
||||
SetupAsLocalPlayer();
|
||||
|
||||
RR.ClientTick += OnClientTick;
|
||||
} else {
|
||||
SetupAsRemotePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn() {
|
||||
base.OnNetworkDespawn();
|
||||
|
||||
if (IsOwner) {
|
||||
RR.ClientTick -= OnClientTick;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupAsLocalPlayer() {
|
||||
m_Camera.enabled = true;
|
||||
m_Camera.Camera.enabled = true;
|
||||
m_Locomotion.enabled = true;
|
||||
|
||||
if (TryGetComponent(out Rigidbody rbody)) {
|
||||
rbody.isKinematic = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupAsRemotePlayer() {
|
||||
m_Camera.enabled = false;
|
||||
m_Camera.Camera.enabled = false;
|
||||
m_Locomotion.enabled = false;
|
||||
|
||||
if (TryGetComponent(out Rigidbody rbody)) {
|
||||
rbody.isKinematic = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGainedOwnership() {
|
||||
base.OnGainedOwnership();
|
||||
SetupAsLocalPlayer();
|
||||
}
|
||||
|
||||
public override void OnLostOwnership() {
|
||||
base.OnLostOwnership();
|
||||
SetupAsRemotePlayer();
|
||||
}
|
||||
|
||||
|
||||
void Update() {
|
||||
if (!IsSpawned) {
|
||||
return;
|
||||
@@ -232,90 +167,48 @@ namespace RebootReality.jelycho.Player {
|
||||
m_HeadAimTargetTransform.position = (float3) m_HeadBoneTransform.position + LookDirection * 5.0f;
|
||||
}
|
||||
|
||||
void OwnerTick() {
|
||||
// Camera Stuff
|
||||
m_Camera.Tick();
|
||||
// @MARK: NetworkBehaviour callbacks
|
||||
public override void OnNetworkSpawn() {
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (m_Locomotion.IsGrounded &&
|
||||
m_Locomotion.SpeedXZ >= m_Locomotion.runSpeed * m_EnableCameraBobbingPercentThreshold) {
|
||||
if (m_Locomotion.IsSprinting) {
|
||||
m_TargetCameraBobbing = m_SprintCameraBobbing;
|
||||
} else {
|
||||
m_TargetCameraBobbing = m_RunCameraBobbing;
|
||||
}
|
||||
} else {
|
||||
m_TargetCameraBobbing = m_IdleCameraBobbing;
|
||||
if (IsServer) {
|
||||
Inventory.OnItemPickedUp += OnItemPickedUp;
|
||||
Inventory.OnItemDropped += OnItemDropped;
|
||||
}
|
||||
|
||||
m_CurrentCameraBobbing = Mathf.MoveTowards(m_CurrentCameraBobbing,
|
||||
m_TargetCameraBobbing,
|
||||
m_CameraBobbingTransitionSpeed * Time.deltaTime);
|
||||
m_Camera.SetBobbing(m_CurrentCameraBobbing);
|
||||
|
||||
m_CameraSpring.UpdateSpring(Time.deltaTime,
|
||||
m_CharacterForwardTransform.up,
|
||||
m_CharacterForwardTransform.right,
|
||||
m_CharacterForwardTransform.forward);
|
||||
|
||||
|
||||
UpdateAnimator(m_Locomotion.Velocity);
|
||||
}
|
||||
|
||||
void RemoteTick() {
|
||||
Vector3 targetPosition = m_NetworkState.Position;
|
||||
|
||||
if ((transform.position - m_NetworkState.Position).sqrMagnitude <
|
||||
m_MinTeleportDistance * m_MinTeleportDistance) {
|
||||
targetPosition = Vector3.MoveTowards(transform.position,
|
||||
m_NetworkState.Position,
|
||||
m_Locomotion.runSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
m_Locomotion.WarpTo(targetPosition);
|
||||
|
||||
m_Camera.Pitch = m_NetworkState.LookPitch;
|
||||
m_Camera.Yaw = m_NetworkState.LookYaw;
|
||||
|
||||
UpdateAnimator(m_NetworkState.Velocity);
|
||||
}
|
||||
|
||||
void OnClientTick() {
|
||||
PlayerActorState state = new PlayerActorState {
|
||||
Position = transform.position,
|
||||
LookPitch = m_Camera.Pitch,
|
||||
LookYaw = m_Camera.Yaw,
|
||||
IsGrounded = m_Locomotion.IsGrounded,
|
||||
Velocity = m_Locomotion.Velocity
|
||||
};
|
||||
|
||||
UpdatePlayerStateRpc(state);
|
||||
}
|
||||
|
||||
PlayerActorState m_NetworkState;
|
||||
|
||||
[Rpc(SendTo.NotMe)]
|
||||
void UpdatePlayerStateRpc(PlayerActorState state) {
|
||||
if (IsOwner) {
|
||||
return;
|
||||
}
|
||||
SetupAsOwner();
|
||||
|
||||
m_NetworkState = state;
|
||||
}
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void WarpToServerRpc(Vector3 position) {
|
||||
WarpToClientRpc(position);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void WarpToClientRpc(Vector3 position) {
|
||||
if (IsOwner) {
|
||||
m_Locomotion.WarpTo(position);
|
||||
RR.ClientTick += SyncActorState;
|
||||
} else {
|
||||
transform.position = position;
|
||||
SetupAsRemote();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn() {
|
||||
base.OnNetworkDespawn();
|
||||
|
||||
if (IsServer) {
|
||||
Inventory.OnItemPickedUp -= OnItemPickedUp;
|
||||
Inventory.OnItemDropped -= OnItemDropped;
|
||||
}
|
||||
|
||||
if (IsOwner) {
|
||||
RR.ClientTick -= SyncActorState;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGainedOwnership() {
|
||||
base.OnGainedOwnership();
|
||||
SetupAsOwner();
|
||||
}
|
||||
|
||||
public override void OnLostOwnership() {
|
||||
base.OnLostOwnership();
|
||||
SetupAsRemote();
|
||||
}
|
||||
|
||||
// @MARK: API to be used by owner
|
||||
public void SetSprint(bool isSprinting) {
|
||||
m_Locomotion.SetSprint(isSprinting);
|
||||
}
|
||||
@@ -351,13 +244,13 @@ namespace RebootReality.jelycho.Player {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetBeaconPosition(out Vector3 beaconPosition)) {
|
||||
SetAnimatorTriggerRpc(AnimatorParamHashes.Throw);
|
||||
|
||||
if (RR.World.Context is WorldContext worldContext) {
|
||||
worldContext.BaseManager.TrySpawnBeacon(beaconPosition);
|
||||
}
|
||||
}
|
||||
// if (TryGetBeaconPosition(out Vector3 beaconPosition)) {
|
||||
// SetAnimatorTriggerRpc(AnimatorParamHashes.Throw);
|
||||
//
|
||||
// if (RR.World.Context is WorldContext worldContext) {
|
||||
// worldContext.BaseManager.TrySpawnBeacon(beaconPosition);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public void SecondaryAction() {
|
||||
@@ -375,28 +268,119 @@ namespace RebootReality.jelycho.Player {
|
||||
return;
|
||||
}
|
||||
|
||||
IInteractable interactable = m_InteractablesSensor.Sense();
|
||||
if (interactable is not null) {
|
||||
interactable.Interact();
|
||||
if (m_TargetInteractable.Value is ItemActor itemActor) {
|
||||
Pickup(itemActor);
|
||||
} else if (m_TargetInteractable.Value is not null) {
|
||||
m_TargetInteractable.Value.Interact();
|
||||
SetAnimatorTriggerRpc(AnimatorParamHashes.Throw);
|
||||
}
|
||||
}
|
||||
|
||||
// @MARK: Owner
|
||||
void SetupAsOwner() {
|
||||
m_Camera.enabled = true;
|
||||
m_Camera.Camera.enabled = true;
|
||||
m_Locomotion.enabled = true;
|
||||
|
||||
[Rpc(SendTo.Everyone)]
|
||||
void SetAnimatorTriggerRpc(int hash) {
|
||||
m_Animator.SetTrigger(hash);
|
||||
if (TryGetComponent(out Rigidbody rbody)) {
|
||||
rbody.isKinematic = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TryGetInteractable(out IInteractable interactable) {
|
||||
GameObject pickedGameObject = m_Camera.Sensor.Sense();
|
||||
if (pickedGameObject != null && pickedGameObject.TryGetComponent(out interactable)) {
|
||||
return true;
|
||||
void OwnerTick() {
|
||||
// Camera Stuff
|
||||
m_Camera.Tick();
|
||||
|
||||
if (m_Locomotion.IsGrounded &&
|
||||
m_Locomotion.SpeedXZ >= m_Locomotion.runSpeed * m_EnableCameraBobbingPercentThreshold) {
|
||||
if (m_Locomotion.IsSprinting) {
|
||||
m_TargetCameraBobbing = m_SprintCameraBobbing;
|
||||
} else {
|
||||
m_TargetCameraBobbing = m_RunCameraBobbing;
|
||||
}
|
||||
} else {
|
||||
m_TargetCameraBobbing = m_IdleCameraBobbing;
|
||||
}
|
||||
|
||||
interactable = null;
|
||||
return false;
|
||||
m_CurrentCameraBobbing = Mathf.MoveTowards(m_CurrentCameraBobbing,
|
||||
m_TargetCameraBobbing,
|
||||
m_CameraBobbingTransitionSpeed * Time.deltaTime);
|
||||
m_Camera.SetBobbing(m_CurrentCameraBobbing);
|
||||
|
||||
m_CameraSpring.UpdateSpring(Time.deltaTime,
|
||||
m_CharacterForwardTransform.up,
|
||||
m_CharacterForwardTransform.right,
|
||||
m_CharacterForwardTransform.forward);
|
||||
|
||||
|
||||
UpdateAnimator(m_Locomotion.Velocity);
|
||||
|
||||
IInteractable interactable = m_InteractablesSensor.Sense();
|
||||
if (interactable != m_TargetInteractable.Value) {
|
||||
m_TargetInteractable.Value = interactable;
|
||||
}
|
||||
}
|
||||
|
||||
void SyncActorState() {
|
||||
PlayerActorState state = new PlayerActorState {
|
||||
Position = transform.position,
|
||||
LookPitch = m_Camera.Pitch,
|
||||
LookYaw = m_Camera.Yaw,
|
||||
IsGrounded = m_Locomotion.IsGrounded,
|
||||
Velocity = m_Locomotion.Velocity
|
||||
};
|
||||
|
||||
UpdatePlayerStateRpc(state);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.NotMe)]
|
||||
void UpdatePlayerStateRpc(PlayerActorState state) {
|
||||
if (IsOwner) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_NetworkState = state;
|
||||
}
|
||||
|
||||
// @MARK: Remote
|
||||
void SetupAsRemote() {
|
||||
m_Camera.enabled = false;
|
||||
m_Camera.Camera.enabled = false;
|
||||
m_Locomotion.enabled = false;
|
||||
|
||||
if (TryGetComponent(out Rigidbody rbody)) {
|
||||
rbody.isKinematic = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTick() {
|
||||
Vector3 targetPosition = m_NetworkState.Position;
|
||||
|
||||
if ((transform.position - m_NetworkState.Position).sqrMagnitude <
|
||||
m_MinTeleportDistance * m_MinTeleportDistance) {
|
||||
targetPosition = Vector3.MoveTowards(transform.position,
|
||||
m_NetworkState.Position,
|
||||
m_Locomotion.runSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
m_Locomotion.WarpTo(targetPosition);
|
||||
|
||||
m_Camera.Pitch = m_NetworkState.LookPitch;
|
||||
m_Camera.Yaw = m_NetworkState.LookYaw;
|
||||
|
||||
UpdateAnimator(m_NetworkState.Velocity);
|
||||
}
|
||||
|
||||
// @MARK: Server
|
||||
void OnItemDropped(ItemActor item) {
|
||||
item.SetHidden(false);
|
||||
}
|
||||
|
||||
void OnItemPickedUp(ItemActor item) {
|
||||
item.SetHidden(true);
|
||||
}
|
||||
|
||||
// @MARK: Sensors
|
||||
bool TryGetBeaconPosition(out Vector3 position) {
|
||||
Ray ray = new Ray(m_Camera.Camera.transform.position, m_Camera.Camera.transform.forward);
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, m_BeaconPlacementMaxDistance, m_BeaconPlacementLayerMask) &&
|
||||
@@ -409,6 +393,87 @@ namespace RebootReality.jelycho.Player {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @MARK: Networked methods
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void WarpToServerRpc(Vector3 position) {
|
||||
WarpToClientRpc(position);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void WarpToClientRpc(Vector3 position) {
|
||||
if (IsOwner) {
|
||||
m_Locomotion.WarpTo(position);
|
||||
} else {
|
||||
transform.position = position;
|
||||
}
|
||||
}
|
||||
|
||||
// @MARK: Inventory
|
||||
void Pickup(ItemActor actor) {
|
||||
PickupItemRpc(actor.ActorID);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
void PickupItemRpc(ulong actorID) {
|
||||
Actor actor = RR.FindSpawnedActor(actorID);
|
||||
if (actor is null) {
|
||||
s_Logger.Error($"Actor with ID {actorID} not found for pickup.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor is ItemActor itemActor) {
|
||||
if (Inventory.TryPickup(itemActor)) {
|
||||
SetAnimatorTriggerRpc(AnimatorParamHashes.Attack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectPreviousItemSlot() {
|
||||
if (!IsOwner) {
|
||||
s_Logger.Error("Only the owner can change inventory selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedInventorySlot.Value > 0) {
|
||||
SelectedInventorySlot.Value--;
|
||||
} else {
|
||||
SelectedInventorySlot.Value = Inventory.SlotsCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectNextItemSlot() {
|
||||
if (!IsOwner) {
|
||||
s_Logger.Error("Only the owner can change inventory selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedInventorySlot.Value < Inventory.SlotsCount - 1) {
|
||||
SelectedInventorySlot.Value++;
|
||||
} else {
|
||||
SelectedInventorySlot.Value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectItemSlot(int slotIndex) {
|
||||
if (!IsOwner) {
|
||||
s_Logger.Error("Only the owner can change inventory selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (slotIndex < 0 || slotIndex >= Inventory.SlotsCount) {
|
||||
s_Logger.Error($"Invalid slot index {slotIndex}. Must be between 0 and {Inventory.SlotsCount - 1}.");
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedInventorySlot.Value = slotIndex;
|
||||
}
|
||||
|
||||
// @MARK: Animations
|
||||
[Rpc(SendTo.Everyone)]
|
||||
void SetAnimatorTriggerRpc(int hash) {
|
||||
m_Animator.SetTrigger(hash);
|
||||
}
|
||||
|
||||
struct AnimatorParamHashes {
|
||||
public static readonly int VelocityForwardNormalized = Animator.StringToHash("VelocityForwardNormalized");
|
||||
public static readonly int VelocityRightNormalized = Animator.StringToHash("VelocityRightNormalized");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using R3;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootReality.jelycho.Player.HUD;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
@@ -15,6 +17,10 @@ namespace RebootReality.jelycho.Player {
|
||||
[SerializeField] Config m_Config;
|
||||
|
||||
PlayerActor m_Actor;
|
||||
PlayerHUD m_HUD;
|
||||
|
||||
DisposableBag m_OwnerActorDisposableBag = new DisposableBag();
|
||||
IDisposable m_TargetInteractableLabelDisposable;
|
||||
|
||||
public override void OnNetworkSpawn() {
|
||||
base.OnNetworkSpawn();
|
||||
@@ -22,6 +28,8 @@ namespace RebootReality.jelycho.Player {
|
||||
if (IsOwner) {
|
||||
RR.Input.LockCursor();
|
||||
RR.Input.EnableControls();
|
||||
|
||||
m_HUD = Instantiate(m_Config.playerHUDPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +39,13 @@ namespace RebootReality.jelycho.Player {
|
||||
if (IsOwner) {
|
||||
RR.Input.UnlockCursor();
|
||||
RR.Input.DisableControls();
|
||||
|
||||
if (m_HUD != null) {
|
||||
Destroy(m_HUD.gameObject);
|
||||
m_HUD = null;
|
||||
}
|
||||
|
||||
m_OwnerActorDisposableBag.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +69,31 @@ namespace RebootReality.jelycho.Player {
|
||||
m_Actor.WarpToServerRpc(worldContext.PlayerSpawnPoint.position);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsOwner) {
|
||||
m_OwnerActorDisposableBag.Dispose();
|
||||
m_OwnerActorDisposableBag = new DisposableBag();
|
||||
m_Actor.TargetInteractable.Subscribe(interactable => {
|
||||
if (m_TargetInteractableLabelDisposable != null) {
|
||||
m_TargetInteractableLabelDisposable.Dispose();
|
||||
m_TargetInteractableLabelDisposable = null;
|
||||
}
|
||||
|
||||
if (interactable is MonoBehaviour mb) {
|
||||
m_TargetInteractableLabelDisposable =
|
||||
m_HUD.ObjectsLabels.CreateLabel(mb.transform, mb.name);
|
||||
}
|
||||
})
|
||||
.AddTo(ref m_OwnerActorDisposableBag);
|
||||
|
||||
m_HUD.SetPlayerActor(m_Actor);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void SetNullActorClientRpc() {
|
||||
m_OwnerActorDisposableBag.Dispose();
|
||||
m_OwnerActorDisposableBag = new DisposableBag();
|
||||
m_Actor = null;
|
||||
}
|
||||
|
||||
@@ -97,14 +133,31 @@ namespace RebootReality.jelycho.Player {
|
||||
if (m_Config.secondaryActionReference.action.WasReleasedThisFrame()) {
|
||||
m_Actor.SecondaryAction();
|
||||
}
|
||||
|
||||
|
||||
if (m_Config.interactActionReference.action.WasReleasedThisFrame()) {
|
||||
m_Actor.Interact();
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_Config.inventorySlotSelectActionReferences.Length; i++) {
|
||||
if (m_Config.inventorySlotSelectActionReferences[i].action.WasReleasedThisFrame()) {
|
||||
m_Actor.SelectItemSlot(i);
|
||||
}
|
||||
}
|
||||
|
||||
float slotChangeAxis = m_Config.inventorySlotChangeActionReference.action.ReadValue<float>();
|
||||
if (math.abs(slotChangeAxis) > 0.5f) {
|
||||
if (slotChangeAxis > 0) {
|
||||
m_Actor.SelectNextItemSlot();
|
||||
} else {
|
||||
m_Actor.SelectPreviousItemSlot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Config {
|
||||
public PlayerHUD playerHUDPrefab;
|
||||
|
||||
public InputActionReference moveActionReference;
|
||||
public InputActionReference lookActionReference;
|
||||
public InputActionReference jumpActionReference;
|
||||
@@ -113,6 +166,9 @@ namespace RebootReality.jelycho.Player {
|
||||
public InputActionReference primaryActionReference;
|
||||
public InputActionReference secondaryActionReference;
|
||||
public InputActionReference interactActionReference;
|
||||
|
||||
public InputActionReference[] inventorySlotSelectActionReferences;
|
||||
public InputActionReference inventorySlotChangeActionReference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user