Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ This template contains the following:
| [Best Practices: Coding Standards](https://www.samuelasherrivello.com/best-practices) | Guidelines for consistent code style and format. |
| [Best Practices: Project Structure](https://www.samuelasherrivello.com/best-practices) | Recommended project structure and organization. |
| [Cinemachine](https://docs.unity3d.com/Packages/com.unity.cinemachine@latest) | Advanced camera system for dynamic shots. |
| [Cysharp/R3](https://github.com/Cysharp/R3) | Reactive Extensions for Unity providing ReactiveProperty and observables. |
| [Physics](https://docs.unity3d.com/Manual/PhysicsSection.html) | Physics simulation for 2D and 3D games. |
| [ProBuilder](https://docs.unity3d.com/Packages/com.unity.probuilder@latest) | 3D modeling and level design toolset. |
| [Rendering: Post-Processing](https://docs.unity3d.com/Packages/com.unity.postprocessing@latest) | Visual effects like color grading and bloom. |
Expand Down
3 changes: 2 additions & 1 deletion Unity/Assets/Scripts/Runtime/RMC.MyProject.Runtime.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"references": [
"GUID:75469ad4d38634e559750d17036d5f7c",
"GUID:8d0b5587a0a8ae741afbfe3a79c2872a",
"GUID:9f522e882e6bd48429f616f259fa6318"
"GUID:9f522e882e6bd48429f616f259fa6318",
"Cysharp.R3"
],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Cysharp.R3;
using RMC.Audio;
using RMC.MyProject.UI;
using UnityEngine;
Expand Down Expand Up @@ -32,7 +33,8 @@ public int Score
set
{
_score = value;
HudUI.ScoreLabel.text = $"Score: {_score:000}/{ScoreMax:000}";
// Update the reactive property, which will trigger the subscription
_reactiveScore.Value = value;
}
}

Expand All @@ -45,7 +47,8 @@ public int Lives
set
{
_lives = value;
HudUI.LivesLabel.text = $"Lives: {_lives:000}/{LivesMax:000}";
// Update the reactive property, which will trigger the subscription
_reactiveLives.Value = value;
}
}

Expand Down Expand Up @@ -107,6 +110,10 @@ private set
private bool _isEnabledInput = true;
private bool _isPlayerGrounded = false;

// R3 Reactive Property Demo
private ReactiveProperty<int> _reactiveScore = new ReactiveProperty<int>(0);
private ReactiveProperty<int> _reactiveLives = new ReactiveProperty<int>(LivesMax);

// Audio
private const string PlayerResetAudioClip = "ItemRead01";
private const string GameWinAudioClip = "Music_Win01";
Expand All @@ -122,6 +129,21 @@ protected void Start()
{
Debug.Log($"{GetType().Name}.Start()");

// R3 ReactiveProperty Demo - Subscribe to changes
_reactiveScore.Subscribe(newScore =>
{
Debug.Log($"[R3 Demo] Reactive Score changed to: {newScore}");
// Update the UI when the reactive score changes
HudUI.ScoreLabel.text = $"Score: {newScore:000}/{ScoreMax:000}";
}).AddTo(this);

_reactiveLives.Subscribe(newLives =>
{
Debug.Log($"[R3 Demo] Reactive Lives changed to: {newLives}");
// Update the UI when the reactive lives changes
HudUI.LivesLabel.text = $"Lives: {newLives:000}/{LivesMax:000}";
}).AddTo(this);

// Input
_movePlayerInputAction = InputSystem.actions.FindAction("MovePlayer");
_jumpPlayerInputAction = InputSystem.actions.FindAction("JumpPlayer");
Expand Down Expand Up @@ -158,6 +180,16 @@ private void SetTitle()
HudUI.TitleLabel.text = $"{SceneManager.GetActiveScene().name} ({themeName})";
}

/// <summary>
/// Runs when the GameObject is destroyed. Clean up resources.
/// </summary>
protected void OnDestroy()
{
// Dispose the ReactiveProperties when the GameObject is destroyed
_reactiveScore?.Dispose();
_reactiveLives?.Dispose();
}

/// <summary>
/// Runs every frame. Use for input/physics/gameplay
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"references": [
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:1bd920aa2cc01364d8e15ad9ca79abf3"
"GUID:1bd920aa2cc01364d8e15ad9ca79abf3",
"Cysharp.R3"
],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System.Collections;
using Cysharp.R3;
using NUnit.Framework;
using RMC.MyProject.Scenes;
using UnityEngine;
using UnityEngine.TestTools;

namespace RMC.MyProject.Tests
{
/// <summary>
/// Tests for R3 ReactiveProperty integration in Scene01_Intro
/// </summary>
[Category("RMC.MyProject.R3")]
public class R3ReactivePropertyTest
{
// Fields ----------------------------------------
private GameObject _parentGameObject = null;
private Scene01_Intro _scene01Intro = null;

// Initialization --------------------------------

/// <summary>
/// Optional. Called before every [Test] method
/// </summary>
[SetUp]
public void Setup()
{
_parentGameObject = new GameObject();
_scene01Intro = _parentGameObject.AddComponent<Scene01_Intro>();
}

/// <summary>
/// Optional. Called after every [Test] method
/// </summary>
[TearDown]
public void TearDown()
{
if (_parentGameObject != null)
{
if (Application.isPlaying)
{
// Unity prefers this while playing
GameObject.Destroy(_parentGameObject);
}
else
{
// Unity prefers this while NOT playing
GameObject.DestroyImmediate(_parentGameObject, false);
}

_parentGameObject = null;
_scene01Intro = null;
}
}

// Methods ---------------------------------------

/// <summary>
/// Test that ReactiveProperty can be created and disposed
/// </summary>
[Test]
public void ReactiveProperty_CanBeCreatedAndDisposed()
{
// Arrange & Act
var reactiveProperty = new ReactiveProperty<int>(42);

// Assert
Assert.IsNotNull(reactiveProperty);
Assert.AreEqual(42, reactiveProperty.Value);

// Cleanup
reactiveProperty.Dispose();
}

/// <summary>
/// Test that ReactiveProperty subscription works
/// </summary>
[Test]
public void ReactiveProperty_SubscriptionTriggersOnValueChange()
{
// Arrange
var reactiveProperty = new ReactiveProperty<int>(0);
int callbackCount = 0;
int lastValue = -1;

// Act
var subscription = reactiveProperty.Subscribe(value =>
{
callbackCount++;
lastValue = value;
});

// Initial subscription should trigger immediately
Assert.AreEqual(1, callbackCount);
Assert.AreEqual(0, lastValue);

// Change value
reactiveProperty.Value = 42;

// Assert
Assert.AreEqual(2, callbackCount);
Assert.AreEqual(42, lastValue);

// Cleanup
subscription.Dispose();
reactiveProperty.Dispose();
}

/// <summary>
/// A [UnityTest] that verifies R3 works in Unity context
/// </summary>
[UnityTest]
public IEnumerator ReactiveProperty_WorksInUnityContext()
{
// Arrange
var reactiveProperty = new ReactiveProperty<string>("initial");
bool subscriptionTriggered = false;
string receivedValue = null;

// Act
reactiveProperty.Subscribe(value =>
{
subscriptionTriggered = true;
receivedValue = value;
});

// Wait a frame
yield return null;

// Assert initial state
Assert.IsTrue(subscriptionTriggered);
Assert.AreEqual("initial", receivedValue);

// Reset and test value change
subscriptionTriggered = false;
reactiveProperty.Value = "changed";

// Wait a frame
yield return null;

// Assert
Assert.IsTrue(subscriptionTriggered);
Assert.AreEqual("changed", receivedValue);

// Cleanup
reactiveProperty.Dispose();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Unity/Packages/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"com.cysharp.r3": "https://github.com/Cysharp/R3.git",
"com.rmc.rmc-audio": "1.8.4",
"com.rmc.rmc-core": "1.9.5",
"com.rmc.rmc-readme": "1.2.2",
Expand Down