Mikameel
Mikameel2w ago

networked buttons / votes

I have 2 buttons in my Unity game One for team blue and one for team red. each player can vote which team is winning. But you can only vote for 1 team. So its either team red or team blue. I want to show the livescore in text that is networked. I want to network the current votes for each time. So the only thing that is networked should be the amount of votes each team has. and a player can only vote for one team.
9 Replies
kapa.ai
kapa.ai2w ago
Hi @Mikameel. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To implement networked buttons for team voting in your Unity game using Normcore, you can create a custom RealtimeComponent to synchronize the vote counts. Here's how you can approach this: 1. First, create a custom RealtimeModel to store the vote counts:
using Normal.Realtime;
using UnityEngine;

[RealtimeModel]
public partial class VoteCountModel
{
[RealtimeProperty(1, true)]
private int _blueVotes;

[RealtimeProperty(2, true)]
private int _redVotes;
}
using Normal.Realtime;
using UnityEngine;

[RealtimeModel]
public partial class VoteCountModel
{
[RealtimeProperty(1, true)]
private int _blueVotes;

[RealtimeProperty(2, true)]
private int _redVotes;
}
2. Then, create a custom RealtimeComponent to manage the voting logic:
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteCountModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;

private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

public void Vote(bool forBlue)
{
if (hasVoted) return;

if (forBlue)
model.blueVotes++;
else
model.redVotes++;

hasVoted = true;
UpdateUI();
}

protected override void OnRealtimeModelReplaced(VoteCountModel previousModel, VoteCountModel currentModel)
{
if (previousModel != null)
{
previousModel.blueVotesDidChange -= UpdateUI;
previousModel.redVotesDidChange -= UpdateUI;
}

if (currentModel != null)
{
currentModel.blueVotesDidChange += UpdateUI;
currentModel.redVotesDidChange += UpdateUI;
UpdateUI();
}
}

private void UpdateUI()
{
scoreText.text = $"Blue: {model.blueVotes} - Red: {model.redVotes}";
}
}
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteCountModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;

private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

public void Vote(bool forBlue)
{
if (hasVoted) return;

if (forBlue)
model.blueVotes++;
else
model.redVotes++;

hasVoted = true;
UpdateUI();
}

protected override void OnRealtimeModelReplaced(VoteCountModel previousModel, VoteCountModel currentModel)
{
if (previousModel != null)
{
previousModel.blueVotesDidChange -= UpdateUI;
previousModel.redVotesDidChange -= UpdateUI;
}

if (currentModel != null)
{
currentModel.blueVotesDidChange += UpdateUI;
currentModel.redVotesDidChange += UpdateUI;
UpdateUI();
}
}

private void UpdateUI()
{
scoreText.text = $"Blue: {model.blueVotes} - Red: {model.redVotes}";
}
}
3. In your Unity scene, add a Realtime component to an empty GameObject. 4. Create UI elements for the blue and red buttons, and a Text element for the score display. 5. Add the VoteManager component to a GameObject in your scene, and assign the UI elements in the inspector. This implementation will synchronize the vote counts across all clients connected to the same Normcore room. Each player can only vote once, and the live score will be updated in real-time for all players. For more information on creating custom RealtimeComponents and synchronizing data with Normcore, you can refer to the following resources: Synchronizing Custom Data RealtimeComponent Remember to compile your VoteCountModel after creating it, as explained in the Normcore documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
It cannot resolve blueVotesDidChange It cannot resolve blueVotesDidChange @Normcore Assistant
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize for the confusion in my previous response. You're right that the blueVotesDidChange and redVotesDidChange events are not automatically generated in Normcore 2. Let me provide you with the correct implementation: To resolve the issue with blueVotesDidChange, we need to modify our approach slightly. In Normcore 2, we can use the PropertyChanged event instead. Here's how you can update the VoteManager script:
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteCountModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;

private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

public void Vote(bool forBlue)
{
if (hasVoted) return;

if (forBlue)
model.blueVotes++;
else
model.redVotes++;

hasVoted = true;
UpdateUI();
}

protected override void OnRealtimeModelReplaced(VoteCountModel previousModel, VoteCountModel currentModel)
{
if (previousModel != null)
{
previousModel.PropertyChanged -= OnModelPropertyChanged;
}

if (currentModel != null)
{
currentModel.PropertyChanged += OnModelPropertyChanged;
UpdateUI();
}
}

private void OnModelPropertyChanged(IRealtimeModel model, string propertyName)
{
if (propertyName == "blueVotes" || propertyName == "redVotes")
{
UpdateUI();
}
}

private void UpdateUI()
{
scoreText.text = $"Blue: {model.blueVotes} - Red: {model.redVotes}";
}
}
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteCountModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;

private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

public void Vote(bool forBlue)
{
if (hasVoted) return;

if (forBlue)
model.blueVotes++;
else
model.redVotes++;

hasVoted = true;
UpdateUI();
}

protected override void OnRealtimeModelReplaced(VoteCountModel previousModel, VoteCountModel currentModel)
{
if (previousModel != null)
{
previousModel.PropertyChanged -= OnModelPropertyChanged;
}

if (currentModel != null)
{
currentModel.PropertyChanged += OnModelPropertyChanged;
UpdateUI();
}
}

private void OnModelPropertyChanged(IRealtimeModel model, string propertyName)
{
if (propertyName == "blueVotes" || propertyName == "redVotes")
{
UpdateUI();
}
}

private void UpdateUI()
{
scoreText.text = $"Blue: {model.blueVotes} - Red: {model.redVotes}";
}
}
In this updated version, we're using the PropertyChanged event instead of specific events for each property. This event is fired whenever any property in the model changes. We check if the changed property is either blueVotes or redVotes, and if so, we update the UI. This approach is consistent with Normcore 2's implementation of RealtimeModel. For more information on working with RealtimeModel and property changes in Normcore 2, you can refer to the Synchronizing Custom Data documentation. Remember to ensure that your VoteCountModel is correctly defined and compiled as shown in the previous response. If you're still encountering issues, double-check that your Normcore version is up to date and that the model has been properly compiled. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
@Normcore Assistant The Propertychanged doesnt work as well. This is my code, it works except that it's not updated unless a local button is pressed. Can I use RPC events for this? I want to set up RPC anyway
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize for the confusion in my previous responses. You're right that the PropertyChanged event might not be working as expected in your case. While Normcore generally discourages the use of RPC (Remote Procedure Call) events, they do provide a way to implement RPC-like functionality using their datastore. This approach can be useful for one-shot effects or events that don't need to be synchronized for late-joining clients. Here's how you can implement an RPC-like event system for your voting scenario using Normcore: 1. First, let's create a model for our voting event:
using Normal.Realtime;
using UnityEngine;

[RealtimeModel]
public partial class VoteEventModel
{
[RealtimeProperty(1, true)] private int _trigger;
[RealtimeProperty(2, true)] private int _senderID;
[RealtimeProperty(3, true)] private bool _isBlueVote;

public void FireEvent(int senderID, bool isBlueVote)
{
this.trigger++;
this.senderID = senderID;
this.isBlueVote = isBlueVote;
}

public delegate void EventHandler(int senderID, bool isBlueVote);
public event EventHandler eventDidFire;

[RealtimeCallback(RealtimeModelEvent.OnDidRead)]
private void DidRead()
{
if (eventDidFire != null && trigger != 0)
eventDidFire(senderID, isBlueVote);
}
}
using Normal.Realtime;
using UnityEngine;

[RealtimeModel]
public partial class VoteEventModel
{
[RealtimeProperty(1, true)] private int _trigger;
[RealtimeProperty(2, true)] private int _senderID;
[RealtimeProperty(3, true)] private bool _isBlueVote;

public void FireEvent(int senderID, bool isBlueVote)
{
this.trigger++;
this.senderID = senderID;
this.isBlueVote = isBlueVote;
}

public delegate void EventHandler(int senderID, bool isBlueVote);
public event EventHandler eventDidFire;

[RealtimeCallback(RealtimeModelEvent.OnDidRead)]
private void DidRead()
{
if (eventDidFire != null && trigger != 0)
eventDidFire(senderID, isBlueVote);
}
}
2. Now, let's update our VoteManager to use this event system:
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteEventModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;
private int blueVotes = 0;
private int redVotes = 0;
private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

protected override void OnRealtimeModelReplaced(VoteEventModel previousModel, VoteEventModel currentModel)
{
if (previousModel != null)
{
previousModel.eventDidFire -= OnVoteEventFired;
}

if (currentModel != null)
{
currentModel.eventDidFire += OnVoteEventFired;
}
}

private void Vote(bool forBlue)
{
if (hasVoted) return;

model.FireEvent(realtime.clientID, forBlue);
hasVoted = true;
}

private void OnVoteEventFired(int senderID, bool isBlueVote)
{
if (isBlueVote)
blueVotes++;
else
redVotes++;

UpdateUI();
}

private void UpdateUI()
{
scoreText.text = $"Blue: {blueVotes} - Red: {redVotes}";
}
}
using Normal.Realtime;
using UnityEngine;
using UnityEngine.UI;

public class VoteManager : RealtimeComponent<VoteEventModel>
{
public Text scoreText;
public Button blueButton;
public Button redButton;

private bool hasVoted = false;
private int blueVotes = 0;
private int redVotes = 0;
private void Awake()
{
blueButton.onClick.AddListener(() => Vote(true));
redButton.onClick.AddListener(() => Vote(false));
}

protected override void OnRealtimeModelReplaced(VoteEventModel previousModel, VoteEventModel currentModel)
{
if (previousModel != null)
{
previousModel.eventDidFire -= OnVoteEventFired;
}

if (currentModel != null)
{
currentModel.eventDidFire += OnVoteEventFired;
}
}

private void Vote(bool forBlue)
{
if (hasVoted) return;

model.FireEvent(realtime.clientID, forBlue);
hasVoted = true;
}

private void OnVoteEventFired(int senderID, bool isBlueVote)
{
if (isBlueVote)
blueVotes++;
else
redVotes++;

UpdateUI();
}

private void UpdateUI()
{
scoreText.text = $"Blue: {blueVotes} - Red: {redVotes}";
}
}
This implementation uses an RPC-like event system to synchronize votes across all clients. When a player votes, it fires an event that all clients receive. Each client then updates its local vote count and UI accordingly. Remember to compile your VoteEventModel after creating it. This approach should update the vote counts on all clients whenever any client votes, without requiring a local button press on each client. For more information on implementing RPC-like events in Normcore, you can refer to the RPC Events Recipe in the Normcore documentation. However, please note that Normcore generally recommends against using RPC messages for most scenarios, as stated in their documentation: Room > Room > RPCs "RPC messages in Normcore are available as a last resort. If you have players that can join a room late, RPC messages make consistent synchronization difficult. Any state modified in response to an RPC message will need to be manually synchronized by your own code." If you're experiencing issues with property synchronization, it might be worth revisiting your original implementation and ensuring that all components are properly set up and connected in your Unity scene. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@365574199844274177> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mikameel
MikameelOP2w ago
Update on this for someone reading this later. I forgot to set the property in the realtimemodel to True on create subscribe event

Did you find this page helpful?