From cd480e6ba301b512bd873caad26338680a033591 Mon Sep 17 00:00:00 2001 From: Bas Date: Sun, 26 Apr 2020 15:38:48 +0200 Subject: [PATCH] Abstracting the movement of sprites as preparation for vehicle sprites. --- src/openrct2-dll/Export/Sprites.cpp | 24 +- .../Scripts/OpenRCT2/Map/PeepController.cs | 248 ++++-------------- .../Scripts/OpenRCT2/Map/SpriteController.cs | 190 ++++++++++++++ .../OpenRCT2/Map/SpriteController.cs.meta | 11 + .../Assets/Scripts/OpenRCT2/OpenRCT2.Lib.cs | 4 + .../Scripts/OpenRCT2/Sprites/ISprite.cs | 13 + .../Scripts/OpenRCT2/Sprites/ISprite.cs.meta | 11 + .../Assets/Scripts/OpenRCT2/Sprites/Peep.cs | 2 +- .../Scripts/OpenRCT2/Sprites/Vehicle.cs | 44 ++++ .../Scripts/OpenRCT2/Sprites/Vehicle.cs.meta | 11 + 10 files changed, 363 insertions(+), 195 deletions(-) create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs.meta create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs.meta create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs create mode 100644 src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs.meta diff --git a/src/openrct2-dll/Export/Sprites.cpp b/src/openrct2-dll/Export/Sprites.cpp index 21c07da550..28202dc9f4 100644 --- a/src/openrct2-dll/Export/Sprites.cpp +++ b/src/openrct2-dll/Export/Sprites.cpp @@ -1,4 +1,3 @@ - #include #include "Openrct2-dll.h" @@ -6,12 +5,15 @@ extern "C" { + // Gets the amount of sprites currently active for the given type. + // All type possibilities are found in the 'SPRITE_LIST'-enum in 'world/Sprite.h'. EXPORT int GetSpriteCount(int spriteType) { return gSpriteListCount[spriteType]; } + // Loads all the peeps into the specified buffer, returns the total amount of peeps loaded. EXPORT int GetAllPeeps(Peep* peeps, int arraySize) { Peep* peep; @@ -28,4 +30,24 @@ extern "C" } return peepCount; } + + + // Loads all the vehicles into the specified buffer, returns the total amount of vehicles loaded. + EXPORT int GetAllVehicles(Vehicle* vehicles, int arraySize) + { + Vehicle* vehicle; + int vehicleCount = 0; + + for (uint16_t i = gSpriteListHead[SPRITE_LIST_VEHICLE]; i != SPRITE_INDEX_NULL; i = vehicle->next) + { + vehicle = &get_sprite(i)->vehicle; + + vehicles[vehicleCount] = *vehicle; + vehicleCount++; + + if (vehicleCount >= arraySize) + break; + } + return vehicleCount; + } } diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/PeepController.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/PeepController.cs index cc620118b9..5d7d54da46 100644 --- a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/PeepController.cs +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/PeepController.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -7,74 +6,8 @@ namespace OpenRCT2.Unity /// /// Controller which moves and updates all the peeps in the park. /// - [RequireComponent(typeof(Map))] - public class PeepController : MonoBehaviour + public class PeepController : SpriteController { - [SerializeField] GameObject peepPrefab; - - - const int MaxPeeps = 8000; - - - Peep[] peepBuffer; - Dictionary peepObjects; - int currentUpdateTick; - - - /// - /// Internal peep object. - /// - class PeepObject - { - public int bufferIndex; - public GameObject gameObject; - public float timeSinceStart; - public int lastUpdate; - public Vector3 from; - public Vector3 towards; - } - - - void Start() - { - peepBuffer = new Peep[MaxPeeps]; - int amount = OpenRCT2.GetAllPeeps(peepBuffer); - peepObjects = new Dictionary(amount); - - for (int i = 0; i < amount; i++) - { - AddPeep(i, ref peepBuffer[i]); - } - } - - - /// - /// Update ~40 per second to get the peep information from OpenRCT2. - /// - void FixedUpdate() - { - currentUpdateTick++; - - int amount = OpenRCT2.GetAllPeeps(peepBuffer); - - for (int i = 0; i < amount; i++) - { - SetPeepPositions(i, ref peepBuffer[i]); - } - } - - - /// - /// Update and lerp the peeps every frame for smoother transitions. - /// - void LateUpdate() - { - foreach (var peep in peepObjects.Values) - { - UpdatePeepPosition(peep); - } - } - /// /// Find the associated peep id for the specified gameobject, or @@ -82,10 +15,10 @@ namespace OpenRCT2.Unity /// public ushort FindPeepIdForGameObject(GameObject peepObject) { - var entry = peepObjects.FirstOrDefault(p => p.Value.gameObject == peepObject); + var entry = spriteObjects.FirstOrDefault(p => p.Value.gameObject == peepObject); int bufferIndex = entry.Value.bufferIndex; - return peepBuffer[bufferIndex].Id; + return spriteBuffer[bufferIndex].Id; } @@ -95,11 +28,61 @@ namespace OpenRCT2.Unity /// public Peep? GetPeepById(ushort peepId) { - if (!peepObjects.TryGetValue(peepId, out PeepObject peepObject)) + if (!spriteObjects.TryGetValue(peepId, out SpriteObject peepObject)) return null; int bufferIndex = peepObject.bufferIndex; - return peepBuffer[bufferIndex]; + return spriteBuffer[bufferIndex]; + } + + + /// + /// Gets all the peep sprites from the dll hook. + /// + protected override int FillSpriteBuffer(Peep[] buffer) + => OpenRCT2.GetAllPeeps(buffer); + + + /// + /// Sets the name and colors of the peep sprite. + /// + protected override SpriteObject AddSprite(int index, ref Peep sprite) + { + SpriteObject spriteObject = base.AddSprite(index, ref sprite); + GameObject obj = spriteObject.gameObject; + + ushort id = sprite.Id; + PeepType type = sprite.type; + obj.name = $"{type} {id}"; + + UpdateColours(obj, sprite); + return spriteObject; + } + + + /// + /// Sets the new start and end positions for this game tick. + /// + /// + protected override SpriteObject UpdateSprite(int index, ref Peep sprite) + { + SpriteObject obj = base.UpdateSprite(index, ref sprite); + + obj.gameObject.GetComponent().UpdateInformation(sprite); + return obj; + } + + + void UpdateColours(GameObject peepObj, Peep peep) + { + GameObject tshirt = peepObj.transform.GetChild(0).gameObject; + GameObject trousers = peepObj.transform.GetChild(1).gameObject; + + var tshirtRenderer = tshirt.GetComponent(); + var trousersRenderer = trousers.GetComponent(); + + tshirtRenderer.material.color = DecodeColour(peep.tshirtColour); + trousersRenderer.material.color = DecodeColour(peep.trousersColour); } @@ -209,126 +192,5 @@ namespace OpenRCT2.Unity } return colourRGB; } - - - void UpdateColours(GameObject peepObj, Peep peep) - { - - GameObject tshirt = peepObj.transform.GetChild(0).gameObject; - GameObject trousers = peepObj.transform.GetChild(1).gameObject; - - var tshirtRenderer = tshirt.GetComponent(); - var trousersRenderer = trousers.GetComponent(); - - tshirtRenderer.material.color = DecodeColour(peep.tshirtColour); - trousersRenderer.material.color = DecodeColour(peep.trousersColour); - - } - - - /// - /// Adds a new peep object to the dictionary. - /// - PeepObject AddPeep(int index, ref Peep peep) - { - ushort id = peep.Id; - PeepType type = peep.type; - GameObject peepObj = Instantiate(peepPrefab, Vector3.zero, Quaternion.identity, transform); - - peepObj.name = $"{type} {id}"; - - UpdateColours(peepObj, peep); - - Vector3 position = Map.CoordsToVector3(peep.Position); - - PeepObject instance = new PeepObject - { - bufferIndex = index, - gameObject = peepObj, - from = position, - towards = position - }; - - peepObjects.Add(peep.Id, instance); - return instance; - } - - - /// - /// Sets the new start and end positions for this game tick. - /// - void SetPeepPositions(int index, ref Peep peep) - { - ushort id = peep.Id; - - if (!peepObjects.TryGetValue(id, out PeepObject obj)) - { - obj = AddPeep(index, ref peep); - } - else - { - obj.bufferIndex = index; - } - - obj.lastUpdate = currentUpdateTick; - - obj.gameObject.GetComponent().UpdateInformation(peep); - - Vector3 target = Map.CoordsToVector3(peep.Position); - - if (obj.towards == target) - return; - - obj.from = obj.towards; - obj.towards = target; - obj.timeSinceStart = Time.timeSinceLevelLoad; - } - - - /// - /// Updates the actual object position by lerping between the information - /// from last game tick. - /// - void UpdatePeepPosition(PeepObject obj) - { - if (obj.lastUpdate < currentUpdateTick) - { - DisablePeep(obj); - return; - } - else - EnablePeep(obj); - - Transform transf = obj.gameObject.transform; - - if (transf.position == obj.towards) - return; - - // Update position - float time = (Time.timeSinceLevelLoad - obj.timeSinceStart) / Time.fixedDeltaTime; - Vector3 lerped = Vector3.Lerp(transf.position, obj.towards, time); - - transf.position = lerped; - - // Update rotation - Vector3 forward = new Vector3(obj.from.x - obj.towards.x, 0, obj.from.z - obj.towards.z); - - if (forward != Vector3.zero) - transf.rotation = Quaternion.LookRotation(forward); - } - - - void EnablePeep(PeepObject obj) - { - if (!obj.gameObject.activeSelf) - obj.gameObject.SetActive(true); - } - - - void DisablePeep(PeepObject obj) - { - if (obj.gameObject.activeSelf) - obj.gameObject.SetActive(false); - } } } diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs new file mode 100644 index 0000000000..569c5ae4cf --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs @@ -0,0 +1,190 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace OpenRCT2.Unity +{ + /// + /// Abstract base class for shared code in moving sprites around. + /// + public abstract class SpriteController : MonoBehaviour where TSprite : ISprite + { + [SerializeField] GameObject spritePrefab; + + + const int MaxSprites = 8000; + + + protected TSprite[] spriteBuffer; + protected Dictionary spriteObjects; + int currentUpdateTick; + + + /// + /// Bind the method to fill the sprite buffer. + /// + protected abstract int FillSpriteBuffer(TSprite[] buffer); + + + /// + /// Initializes the sprite controller. + /// + void Start() + { + spriteBuffer = new TSprite[MaxSprites]; + int amount = FillSpriteBuffer(spriteBuffer); + + spriteObjects = new Dictionary(amount); + + for (int i = 0; i < amount; i++) + { + AddSprite(i, ref spriteBuffer[i]); + } + } + + + /// + /// Update ~40 per second to get the sprite information from OpenRCT2. + /// + void FixedUpdate() + { + currentUpdateTick++; + + int amount = FillSpriteBuffer(spriteBuffer); + + for (int i = 0; i < amount; i++) + { + UpdateSprite(i, ref spriteBuffer[i]); + } + } + + + /// + /// Update and lerp the sprites every frame for smoother transitions. + /// + void LateUpdate() + { + foreach (var sprite in spriteObjects.Values) + { + MoveSprite(sprite); + } + } + + + /// + /// Adds a new peep object to the dictionary. + /// + protected virtual SpriteObject AddSprite(int index, ref TSprite sprite) + { + Vector3 position = Map.CoordsToVector3(sprite.Position); + GameObject peepObj = Instantiate(spritePrefab, position, Quaternion.identity, transform); + + SpriteObject instance = new SpriteObject + { + bufferIndex = index, + gameObject = peepObj, + from = position, + towards = position + }; + + spriteObjects.Add(sprite.Id, instance); + return instance; + } + + + /// + /// Sets the new start and end positions for this game tick. + /// + protected virtual SpriteObject UpdateSprite(int index, ref TSprite sprite) + { + ushort id = sprite.Id; + + if (!spriteObjects.TryGetValue(id, out SpriteObject obj)) + { + obj = AddSprite(index, ref sprite); + } + else + { + obj.bufferIndex = index; + } + + obj.lastUpdate = currentUpdateTick; + + Vector3 target = Map.CoordsToVector3(sprite.Position); + + if (obj.towards != target) + { + obj.from = obj.towards; + obj.towards = target; + obj.timeSinceStart = Time.timeSinceLevelLoad; + } + return obj; + } + + + /// + /// Updates the actual object position by lerping between the information + /// from last game tick. + /// + void MoveSprite(SpriteObject spriteObject) + { + if (spriteObject.lastUpdate < currentUpdateTick) + { + DisableSprite(spriteObject); + return; + } + else + EnableSprite(spriteObject); + + Transform transf = spriteObject.gameObject.transform; + + if (transf.position == spriteObject.towards) + return; + + // Update position + float time = (Time.timeSinceLevelLoad - spriteObject.timeSinceStart) / Time.fixedDeltaTime; + Vector3 lerped = Vector3.Lerp(transf.position, spriteObject.towards, time); + + transf.position = lerped; + + // Update rotation + Vector3 forward = new Vector3(spriteObject.from.x - spriteObject.towards.x, 0, spriteObject.from.z - spriteObject.towards.z); + + if (forward != Vector3.zero) + transf.rotation = Quaternion.LookRotation(forward); + } + + + /// + /// Activates the gameobject of the sprite. + /// + void EnableSprite(SpriteObject spriteObject) + { + if (!spriteObject.gameObject.activeSelf) + spriteObject.gameObject.SetActive(true); + } + + + /// + /// Deactivates the gameobject of the sprite. + /// + void DisableSprite(SpriteObject spriteObject) + { + if (spriteObject.gameObject.activeSelf) + spriteObject.gameObject.SetActive(false); + } + + + /// + /// Internal sprite object. + /// + protected class SpriteObject + { + public int bufferIndex; + public GameObject gameObject; + public float timeSinceStart; + public int lastUpdate; + public Vector3 from; + public Vector3 towards; + } + } +} diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs.meta b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs.meta new file mode 100644 index 0000000000..9e418a7f45 --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Map/SpriteController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 910d2182882387a409c326938ace4804 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/OpenRCT2.Lib.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/OpenRCT2.Lib.cs index 01b5764f1b..04d4537c33 100644 --- a/src/openrct2-unity/Assets/Scripts/OpenRCT2/OpenRCT2.Lib.cs +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/OpenRCT2.Lib.cs @@ -112,6 +112,10 @@ namespace OpenRCT2.Unity static extern int GetAllPeeps([Out] Peep[] elements, int arraySize); + [DllImport(PluginFile, CallingConvention = CallingConvention.Cdecl)] + static extern int GetAllVehicles([Out] Vehicle[] elements, int arraySize); + + /// /// Returns all peeps in the park. /// diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs new file mode 100644 index 0000000000..5aa9ee8bbd --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs @@ -0,0 +1,13 @@ +using UnityEngine; + +namespace OpenRCT2.Unity +{ + /// + /// Generic sprite information. + /// + public interface ISprite + { + ushort Id { get; } + Vector3 Position { get; } + } +} diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs.meta b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs.meta new file mode 100644 index 0000000000..e836dd2cf3 --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/ISprite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80ecc49feebf47d44b54106aeb9581c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Peep.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Peep.cs index dbad706be2..93bb07ff29 100644 --- a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Peep.cs +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Peep.cs @@ -8,7 +8,7 @@ namespace OpenRCT2.Unity /// The struct of a peep, which can be either a guest or a staff member. /// [StructLayout(LayoutKind.Sequential, Size = (256 + Ptr.Size))] - public struct Peep + public struct Peep : ISprite { public SpriteBase sprite; public IntPtr namePtr; // The real name diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs new file mode 100644 index 0000000000..0bbfd3526e --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs @@ -0,0 +1,44 @@ +using System.Runtime.InteropServices; +using UnityEngine; + +namespace OpenRCT2.Unity +{ + /// + /// The struct of a ride vehicle. + /// + [StructLayout(LayoutKind.Sequential, Size = 234)] + public struct Vehicle : ISprite + { + public SpriteBase sprite; + public byte spriteType; + public byte bankRotation; + public int remainingDistance; + public int velocity; + public int acceleration; + public ushort rideId; + public byte vehicleType; + public byte colourBody; + public byte colourTrim; + public ushort trackProgress; // this memory slot is a union with 'var_34' and 'var_35'. + public short trackDirectionOrType; // direction = first 2 bits, type = next 8 bits. + public int trackLocationX; + public int trackLocationY; + public int trackLocationZ; + + // ... + + + /// + /// Returns an id of the vehicle, currently based on the sprite index. + /// + public ushort Id + => sprite.spriteIndex; + + + /// + /// Returns the vehicle's position in RCT2 coordinates. + /// + public Vector3 Position + => new Vector3(sprite.x, sprite.z, sprite.y); + } +} diff --git a/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs.meta b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs.meta new file mode 100644 index 0000000000..a29d02916d --- /dev/null +++ b/src/openrct2-unity/Assets/Scripts/OpenRCT2/Sprites/Vehicle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a77311af412dc2247b76d7143fde237b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: