multiplayer stuff
This commit is contained in:
47
Assets/jelycho/Code/Beacons/BaseManager.cs
Normal file
47
Assets/jelycho/Code/Beacons/BaseManager.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using RebootKit.Engine.Main;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Beacons {
|
||||
public class BaseManager : NetworkBehaviour {
|
||||
[SerializeField] Beacon m_BeaconPrefab;
|
||||
[SerializeField] float m_ConnectionRopeLength = 10.0f;
|
||||
[SerializeField] float m_BeaconSpawnRadius = 15.0f;
|
||||
|
||||
readonly List<Beacon> m_Beacons = new List<Beacon>();
|
||||
|
||||
public IReadOnlyList<Beacon> Beacons => m_Beacons;
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void SpawnBeaconServerRpc(Vector3 position) {
|
||||
if (!IsServer) {
|
||||
Debug.LogWarning("Only the server can spawn beacons.");
|
||||
return;
|
||||
}
|
||||
|
||||
Beacon beacon = Instantiate(m_BeaconPrefab, position, Quaternion.identity);
|
||||
beacon.NetworkObject.Spawn();
|
||||
beacon.GrowClientRpc();
|
||||
|
||||
foreach (Beacon otherBeacon in m_Beacons) {
|
||||
if ((otherBeacon.transform.position - beacon.transform.position).sqrMagnitude <
|
||||
m_BeaconSpawnRadius * m_BeaconSpawnRadius) {
|
||||
|
||||
Vector3 startPosition = otherBeacon.RopeConnectionPoint.position;
|
||||
Vector3 endPosition = beacon.RopeConnectionPoint.position;
|
||||
SpawnRopeRpc(startPosition, endPosition);
|
||||
}
|
||||
}
|
||||
|
||||
m_Beacons.Add(beacon);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Everyone)]
|
||||
void SpawnRopeRpc(Vector3 startPosition, Vector3 endPosition) {
|
||||
if (RR.World.Context is WorldContext worldContext) {
|
||||
worldContext.RopesManager.SpawnLockedRope(m_ConnectionRopeLength, startPosition, endPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Beacons/BaseManager.cs.meta
Normal file
3
Assets/jelycho/Code/Beacons/BaseManager.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac767dd7ed4c424ebad060b7149afac8
|
||||
timeCreated: 1751317007
|
||||
14
Assets/jelycho/Code/Beacons/Beacon.cs
Normal file
14
Assets/jelycho/Code/Beacons/Beacon.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Beacons {
|
||||
public class Beacon : NetworkBehaviour {
|
||||
[SerializeField] BeaconGraphics m_Graphics;
|
||||
[field: SerializeField] public Transform RopeConnectionPoint { get; private set; }
|
||||
|
||||
[ClientRpc]
|
||||
public void GrowClientRpc() {
|
||||
m_Graphics.Grow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using RebootKit.Engine.Services.Simulation;
|
||||
using RebootKit.Engine.Simulation;
|
||||
|
||||
namespace RebootReality.jelycho.Beacons {
|
||||
public class BeaconActor : Actor {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
123
Assets/jelycho/Code/Beacons/BeaconGraphics.cs
Normal file
123
Assets/jelycho/Code/Beacons/BeaconGraphics.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Beacons {
|
||||
public class BeaconGraphics : MonoBehaviour {
|
||||
[SerializeField] BeaconElement[] m_BeaconElements;
|
||||
|
||||
[Range(0.0f, 1.0f)] public float growAmount = 0.5f;
|
||||
[SerializeField] public float growSpeed = 0.5f;
|
||||
|
||||
float m_CurrentGrowAmount = 0.0f;
|
||||
|
||||
void Update() {
|
||||
if (m_CurrentGrowAmount >= 1.0f) {
|
||||
foreach (BeaconElement beaconElement in m_BeaconElements) {
|
||||
if (!beaconElement.usePulsing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float t = (Mathf.Sin(Time.time * beaconElement.pulsingSpeed) + 1.0f) * 0.5f;
|
||||
float scale = Mathf.Lerp(beaconElement.pulsingMinScale,
|
||||
beaconElement.pulsingMaxScale,
|
||||
t);
|
||||
beaconElement.transform.localScale = Vector3.one * scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (Mathf.Approximately(m_CurrentGrowAmount, growAmount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_CurrentGrowAmount = Mathf.MoveTowards(m_CurrentGrowAmount,
|
||||
growAmount,
|
||||
growSpeed * Time.deltaTime);
|
||||
UpdateElements(m_CurrentGrowAmount);
|
||||
}
|
||||
|
||||
void UpdateElements(float amount) {
|
||||
foreach (BeaconElement element in m_BeaconElements) {
|
||||
if (element.useLocalPosition && element.transform is not null) {
|
||||
float localPositionT = math.remap(element.startLocalPositionAt,
|
||||
element.endLocalPositionAt,
|
||||
0.0f,
|
||||
1.0f,
|
||||
amount);
|
||||
Vector3 localPosition =
|
||||
Vector3.Lerp(element.minLocalPosition, element.maxLocalPosition, Mathf.Clamp01(localPositionT));
|
||||
element.transform.localPosition = localPosition;
|
||||
}
|
||||
|
||||
if (element.useScale && element.transform is not null) {
|
||||
float scaleT = math.remap(element.startScaleAt,
|
||||
element.endScaleAt,
|
||||
0.0f,
|
||||
1.0f,
|
||||
amount);
|
||||
Vector3 scale = Vector3.Lerp(element.minScale, element.maxScale, Mathf.Clamp01(scaleT));
|
||||
element.transform.localScale = scale;
|
||||
}
|
||||
|
||||
if (element.useCutoff && element.meshRenderer is not null) {
|
||||
float cutoffT = math.remap(element.startCutoffAt,
|
||||
element.endCutoffAt,
|
||||
0.0f,
|
||||
1.0f,
|
||||
amount);
|
||||
float cutoff = Mathf.Lerp(0.0f, 1.0f, Mathf.Clamp01(cutoffT));
|
||||
|
||||
if (element.flipCutoffDirection) {
|
||||
cutoff = 1.0f - cutoff;
|
||||
}
|
||||
|
||||
Material material = element.meshRenderer.materials[element.materialIndex];
|
||||
material.SetFloat("_Cutoff", cutoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Grow() {
|
||||
m_CurrentGrowAmount = 0.0f;
|
||||
UpdateElements(m_CurrentGrowAmount);
|
||||
growAmount = 1.0f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct BeaconElement {
|
||||
public Transform transform;
|
||||
public MeshRenderer meshRenderer;
|
||||
|
||||
[Header("Local Position(Transform)")]
|
||||
public bool useLocalPosition;
|
||||
|
||||
public Vector3 minLocalPosition;
|
||||
public Vector3 maxLocalPosition;
|
||||
public float startLocalPositionAt;
|
||||
public float endLocalPositionAt;
|
||||
|
||||
[Header("Scale (Transform)")]
|
||||
public bool useScale;
|
||||
public Vector3 minScale;
|
||||
public Vector3 maxScale;
|
||||
|
||||
public float startScaleAt;
|
||||
public float endScaleAt;
|
||||
|
||||
[Header("Mesh Cutoff (MeshRenderer)")]
|
||||
public bool useCutoff;
|
||||
public bool flipCutoffDirection;
|
||||
|
||||
public float startCutoffAt;
|
||||
public float endCutoffAt;
|
||||
|
||||
public int materialIndex;
|
||||
|
||||
[Header("Pulsing (Transform)")]
|
||||
public bool usePulsing;
|
||||
public float pulsingSpeed;
|
||||
public float pulsingMinScale;
|
||||
public float pulsingMaxScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Beacons/BeaconGraphics.cs.meta
Normal file
3
Assets/jelycho/Code/Beacons/BeaconGraphics.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7db2986869814a7495d4f30cbd35d713
|
||||
timeCreated: 1751312364
|
||||
35
Assets/jelycho/Code/Main/GameWorldController.cs
Normal file
35
Assets/jelycho/Code/Main/GameWorldController.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootReality.jelycho.Beacons;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho.Main {
|
||||
public class GameWorldController : NetworkWorldController {
|
||||
static readonly Logger s_Logger = new Logger(nameof(GameWorldController));
|
||||
|
||||
public static GameWorldController Instance { get; private set; }
|
||||
|
||||
[SerializeField] BaseManager m_BaseManager;
|
||||
|
||||
void Awake() {
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void OnDestroy() {
|
||||
Instance = null;
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
public void RequestBeaconSpawnRpc(Vector3 position) {
|
||||
if (!IsServer) {
|
||||
s_Logger.Error("Only the server can spawn beacons.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_BaseManager.SpawnBeaconServerRpc(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Main/GameWorldController.cs.meta
Normal file
3
Assets/jelycho/Code/Main/GameWorldController.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6f280a434549d99d5c2b7f178ba6b6
|
||||
timeCreated: 1751377186
|
||||
@@ -21,12 +21,17 @@ namespace RebootReality.jelycho.Main {
|
||||
|
||||
public class JelychoGame : Game {
|
||||
static readonly Logger s_Logger = new Logger(nameof(JelychoGame));
|
||||
|
||||
[Header("Gameplay")]
|
||||
[SerializeField] GameWorldController m_GameWorldControllerPrefab;
|
||||
|
||||
public GameWorldController m_GameWorldController;
|
||||
|
||||
[Header("Player")]
|
||||
[SerializeField] PlayerController m_PlayerControllerPrefab;
|
||||
[SerializeField] PlayerActor m_PlayerActorPrefab;
|
||||
[SerializeField] string m_MainWorldID = "dev";
|
||||
|
||||
List<PlayerState> m_PlayerStates = new List<PlayerState>();
|
||||
readonly List<PlayerState> m_PlayerStates = new List<PlayerState>();
|
||||
|
||||
void Awake() { }
|
||||
|
||||
@@ -81,9 +86,23 @@ namespace RebootReality.jelycho.Main {
|
||||
|
||||
public override void OnWorldLoaded() {
|
||||
base.OnWorldLoaded();
|
||||
|
||||
if (IsServer) {
|
||||
m_GameWorldController = Instantiate(m_GameWorldControllerPrefab);
|
||||
m_GameWorldController.NetworkObject.Spawn();
|
||||
}
|
||||
|
||||
OnPlayerReadyRpc(NetworkManager.Singleton.LocalClientId);
|
||||
}
|
||||
|
||||
public override void OnWorldUnload() {
|
||||
base.OnWorldUnload();
|
||||
|
||||
if (IsServer) {
|
||||
m_GameWorldController.NetworkObject.Despawn();
|
||||
Destroy(m_GameWorldController.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
void OnPlayerReadyRpc(ulong clientID) {
|
||||
@@ -111,12 +130,6 @@ namespace RebootReality.jelycho.Main {
|
||||
s_Logger.Error($"Player state for client {clientID} not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void OnWorldUnload() {
|
||||
base.OnWorldUnload();
|
||||
|
||||
if (IsServer) { }
|
||||
}
|
||||
}
|
||||
|
||||
public static class JelychoConsoleCommands {
|
||||
@@ -142,12 +155,16 @@ namespace RebootReality.jelycho.Main {
|
||||
|
||||
RR.WriteToConsole("Connecting to server...");
|
||||
|
||||
// if (args.Length != 2) {
|
||||
// RR.Console.WriteToOutput($"Usage: {args[0]} <steam_id>");
|
||||
// return;
|
||||
// }
|
||||
if (args.Length != 2) {
|
||||
RR.WriteToConsole($"Usage: {args[0]} <steam_id>");
|
||||
return;
|
||||
}
|
||||
|
||||
RR.Connect();
|
||||
if (ulong.TryParse(args[1], out ulong steamID)) {
|
||||
RR.ConnectWithSteamID(steamID);
|
||||
} else {
|
||||
RR.WriteToConsole($"Invalid Steam ID: {args[1]}");
|
||||
}
|
||||
}
|
||||
|
||||
[RCCMD("disconnect", "Disconnects from the current server")]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Services.Simulation.Sensors;
|
||||
using RebootKit.Engine.Simulation.Sensors;
|
||||
using RebootReality.jelycho.Main;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Services.Simulation;
|
||||
using RebootKit.Engine.Services.Simulation.Sensors;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootKit.Engine.Simulation.Sensors;
|
||||
using RebootReality.jelycho.Main;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Netcode;
|
||||
@@ -68,6 +71,11 @@ namespace RebootReality.jelycho.Player {
|
||||
[SerializeField] PhysicsObjectDragger m_PhysicsDragger;
|
||||
[SerializeField] FloatRange m_DragDistanceRange = new FloatRange(1.0f, 5.0f);
|
||||
|
||||
[Header("Beacon location picking")]
|
||||
[SerializeField] LayerMask m_BeaconPlacementLayerMask = 0;
|
||||
[SerializeField] float m_BeaconPlacementMaxDistance = 15.0f;
|
||||
[SerializeField] float m_NormalDotUpThreshold = 0.5f;
|
||||
|
||||
[Header("Network")]
|
||||
[SerializeField] float m_MinTeleportDistance = 0.5f;
|
||||
|
||||
@@ -302,13 +310,44 @@ namespace RebootReality.jelycho.Player {
|
||||
}
|
||||
|
||||
public void PrimaryAction() {
|
||||
m_Animator.SetTrigger(AnimatorParamHashes.Attack);
|
||||
if (!IsOwner) {
|
||||
s_Logger.Error("Only the owner can perform primary actions.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetBeaconPosition(out Vector3 beaconPosition)) {
|
||||
SetAnimatorTriggerRpc(AnimatorParamHashes.Throw);
|
||||
|
||||
GameWorldController.Instance.RequestBeaconSpawnRpc(beaconPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public void SecondaryAction() {
|
||||
if (!IsOwner) {
|
||||
s_Logger.Error("Only the owner can perform secondary actions.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Animator.SetTrigger(AnimatorParamHashes.Block);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Everyone)]
|
||||
void SetAnimatorTriggerRpc(int hash) {
|
||||
m_Animator.SetTrigger(hash);
|
||||
}
|
||||
|
||||
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) &&
|
||||
Vector3.Dot(hit.normal, Vector3.up) >= m_NormalDotUpThreshold) {
|
||||
position = hit.point;
|
||||
return true;
|
||||
}
|
||||
|
||||
position = Vector3.zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct AnimatorParamHashes {
|
||||
public static readonly int VelocityForwardNormalized = Animator.StringToHash("VelocityForwardNormalized");
|
||||
public static readonly int VelocityRightNormalized = Animator.StringToHash("VelocityRightNormalized");
|
||||
@@ -317,6 +356,9 @@ namespace RebootReality.jelycho.Player {
|
||||
|
||||
public static readonly int Attack = Animator.StringToHash("Attack");
|
||||
public static readonly int Block = Animator.StringToHash("Block");
|
||||
public static readonly int Throw = Animator.StringToHash("Throw");
|
||||
|
||||
public static readonly int Holding = Animator.StringToHash("Holding");
|
||||
}
|
||||
|
||||
void UpdateAnimator(Vector3 velocity) {
|
||||
@@ -336,6 +378,7 @@ namespace RebootReality.jelycho.Player {
|
||||
m_Animator.SetFloat(AnimatorParamHashes.TurnVelocity, turnVelocity);
|
||||
|
||||
m_Animator.SetBool(AnimatorParamHashes.IsGrounded, m_Locomotion.IsGrounded);
|
||||
m_Animator.SetInteger(AnimatorParamHashes.Holding, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,21 @@ namespace RebootReality.jelycho.Ropes {
|
||||
m_Ropes.Add(rope);
|
||||
}
|
||||
|
||||
public void SpawnLockedRope(float ropeLength, float3 start, float3 end) {
|
||||
int segmentsCount = (int)(ropeLength / m_RopeSegmentLength) + 1;
|
||||
NativeArray<float3> positions = new NativeArray<float3>(segmentsCount, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < segmentsCount; ++i) {
|
||||
float t = (float)i / (segmentsCount - 1);
|
||||
positions[i] = math.lerp(start, end, t);
|
||||
}
|
||||
|
||||
RopeData rope = new RopeData(positions);
|
||||
rope.IsLocked[0] = true;
|
||||
rope.IsLocked[rope.SegmentCount - 1] = true;
|
||||
m_Ropes.Add(rope);
|
||||
}
|
||||
|
||||
// @NOTE: Do not dispose the returned array, it is managed by the RopesManager.
|
||||
public NativeArray<float3> PeekRopePositions(int index) {
|
||||
return m_Ropes[index].Positions;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using RebootKit.Engine.Services.Simulation;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootReality.jelycho.Ropes;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user