- Home /
Networked Coroutine DOESNT behave on client
I have a simple networking issue right now, where I have an authoritative server calling all the shots in a multiplayer game, and when the server calls an RPC (just a simple method to start a coroutine locally) the coroutine runs fine on the server (or in single player) but the client doesnt run the coroutine, even though debug shows the code reaches the same point on client and server!
Heres exactly what happens:
1) The server send out an update to a "planet" (gameobject/scripts running it) like this
networkView.RPC("SendPlanetMessage", RPCMode.OthersBuffered, i, Teams[i], Units[i]); // update clients of this status
2) The RPC fires on the client, and performs the following
[RPC]
void SendPlanetMessage(int planetNumber, int team, int units)
{
// Debug.Log("RPC: SendPlanetMessage - ID: " + networkView.viewID.ToString());
// send all connected users the updates planetary info
if(Teams[planetNumber] != team)
SimpleSwitchColor(planetNumber, team);
Teams[planetNumber] = team;
Units[planetNumber] = units;
}
3) This starts another method locally (client) "SimpleSwitchColor()" like below
void SimpleSwitchColor(int planetNumber, int team)
{
Debug.Log("RPC: SimpleSwitchColor " + planetNumber + " to " + team);
// use it like: networkView.RPC(SimpleSwitchColor(planetNumberString, someColor), RPCMode.AllBuffered);
StartCoroutine(ChangePlanetColor(planetNumber, Planets[planetNumber].renderer.material.color, team, Time.time));
}
4) Which in turn starts this Coroutine for "fading" the gameobjects color (works on server every time)
IEnumerator ChangePlanetColor(int planetNumber, Color startColor, int endTeam, float startTime)
{
Debug.Log("Changing planet color");
// change planet colors
if(endTeam == 1)
{
while(Planets[planetNumber].renderer.material.color != GreenColor && Teams[planetNumber] == endTeam)
{
Color lerpedColor = Color.Lerp(startColor, GreenColor, (Time.time - startTime) / 5);
Planets[planetNumber].renderer.material.color = lerpedColor;
yield return new WaitForSeconds(0.15f);
}
}
if(endTeam == 2)
{
while(Planets[planetNumber].renderer.material.color != RedColor && Teams[planetNumber] == endTeam)
{
Color lerpedColor = Color.Lerp(startColor, RedColor, (Time.time - startTime) / 5);
Planets[planetNumber].renderer.material.color = lerpedColor;
yield return new WaitForSeconds(0.15f);
}
}
if(endTeam == 3)
{
while(Planets[planetNumber].renderer.material.color != BlueColor && Teams[planetNumber] == endTeam)
{
Color lerpedColor = Color.Lerp(startColor, BlueColor, (Time.time - startTime) / 5);
Planets[planetNumber].renderer.material.color = lerpedColor;
yield return new WaitForSeconds(0.15f);
}
}
}
Now, why in the world does it work just fine (locally) when called on server, yet even when its LOCALLY called (through the steps above) on the client, with correct vars, it does NOT change the color of the GO whatsoever... stays the default color...
Is this something strange about unity networking? the console debug entry shows me that the coroutine is getting the right information... maybe im a tired coffee filled coder who needs a break? Am I missing something obvious here?
A couple notes: the server ALSO calles SimpleSwitchColor() when doing this, just not from planet message... I am using Unity 3x latest... testing with both android and windows standalones, sometimes two builds, sometimes a build as server and editor as client, vice versa sometimes...
$$anonymous$$y guess would be that the array "Teams" is not populated correctly on the clients. How do you populate that array?
Edit: Also, does it work on the server if you use RPC$$anonymous$$ode.AllBuffered to switch the color?
It did work on the server as allbuffered (not on the client still), but I decided not to handle it that way because people write that they have problems with allbuffered calls, so I call it locally now on the server, and RPC on the clients. But see, if I just instantly change the color of the target object(again, with this exact RPC), its fine, works no problem, but by trying to implement this fade, is where the problem arises, it fades locally on the server, but then when im calling the coroutine to fade (locally, after RPC comes in) on the client, it doesnt fade! the $$anonymous$$ms are correctly set through checks by the server, sending messages to the client, and when debugging in the editor, I do see these numbers are being correctly set! So the problem has to be in the coroutine being called from outside (a rpc for a method to startcoroutine locally)... maybe this is a networking thing? your not supposed to startcoroutine from RPC call? im not directly calling the coroutines method, like I said, another method (the RPC) starts it...
and like, when im just directly switching colors, there is no coroutine, it just changes the color in the simpleswitchcolor()
Soooooooo, maybe somebody could tell me, how can I easily fade my objects color, and call it from the network, and perhaps an alternative would work better to my attempts above?
or, somebody want to enlighten me in directly calling a coroutine with RPC? is that possible? like networkView.RPC(StartCoroutine(...?
If nobody knows what to try next, I'll hack out another way to get the client to just call these coroutines on itself, based on what the $$anonymous$$ms are set to from the server, rather than doing it this way.
Answer by Bunny83 · May 16, 2013 at 06:29 PM
So what Debug.Logs are you actually seeing? Does SimpleSwitchColor get executed? Does the coroutine get started? What value has "endTeam" on the server / client? Why do you actually check if the "endTeam" is Teams[planetNumber] in the while loop? is it a termination condition? Also is there a reason why you want 6.666 color changes per second?
I would implement this coroutine like this:
IEnumerator ChangePlanetColor(int planetNumber, int endTeam)
{
if (Teams[planetNumber] != endTeam)
yield break;
Material mat = Planets[planetNumber].renderer.material;
Color startColor = mat.color;
Color targetColor = Color.black;
if (endTeam == 1)
targetColor = GreenColor;
else if (endTeam == 2)
targetColor = RedColor;
else if (endTeam == 3)
targetColor = BlueColor;
float t = 0;
while(t < 1.0f)
{
t += Time.deltaTime / 5;
mat.color = Color.Lerp(startColor, targetColor, t);
yield return null;
}
}
I think that's a bit easier and more readable. Also a bit more effecient.
There's nothing wrong the way you call your coroutine. Just add some more debug logs to see what actually get's executed. Just to be sure: Are you sure that the GameObject and the script are activated / enabled? Coroutines won't run on disabled GameObjects / MonoBehaviours.
Ahh I will try this when I get back to it later.
endTeam was an int I used for the $$anonymous$$m it would change to, so 1 was green, 2 was red, and 3 was blue...silly way to handle it but most other parts of my script do it that way, to make easier networking methods sending ints across for $$anonymous$$m info...and I checked it stayed endTeam, because if this happened from two $$anonymous$$ms within the time it ran, they would flicker back and forth between the two $$anonymous$$m colors (coroutine fighting itself lol), and that stopped it from running if another $$anonymous$$m took over the "planet"
I didnt really intend to call it so frequently, I was trying to figure a good balance between speed of updating and speed of changing color, that seemed to work best haha. Your solution seems much better, I'll report back.
EDIT: @bunny83
I tried using your code, and on my client, I get as far as the first debug line, but no other debug lines come up...leads me to think my planets $$anonymous$$ms are incorrect, but the inspector (client running) seems to show good numbers being set (i update the planets $$anonymous$$ms about once a second from another RPC from server)
Here is what I had, again, only got to first debug line?
IEnumerator ChangePlanetColor(int planetNumber, int endTeam)
{
Debug.Log("Starting color change: " + planetNumber + " to " + endTeam);
if (Teams[planetNumber] != endTeam)
yield break;
$$anonymous$$aterial mat = Planets[planetNumber].renderer.material;
Color startColor = mat.color;
Color targetColor = Color.black;
if (endTeam == 1)
{
targetColor = GreenColor;
Debug.Log("SetColorGreen");
}
else if (endTeam == 2)
{
targetColor = RedColor;
Debug.Log("SetColorRed");
}
else if (endTeam == 3)
{
targetColor = BlueColor;
Debug.Log("SetColorBlue");
}
float t = 0;
while(t < 1.0f)
{
t += Time.deltaTime / 5;
mat.color = Color.Lerp(startColor, targetColor, t);
Debug.Log("Step");
yield return null;
}
}
How about using a debug statement like this:
Debug.Log("Planet: " + planetNumber + " endTeam: " + endTeam + " PlanetTeam: " + Teams[planetNumber]);
@bunny83 Awesome, it was that Teams[] was not being set in order before the coroutine ran, causing the coroutine to quit as soon as it starts. Good eye bunny, I would have been looking for that bug for a while on my own :D
I changed the RPC that updates the planets $$anonymous$$ms to occur before the RPC to start the coroutine, and works flawlessly for me now! $$anonymous$$akes a great fade effect from color to color for my game. Thumbs up, especially for the better method!
Bonus sneak peek:
its more so like GalCon which is my direct inspiration for this game, although $$anonymous$$e has unique features to both of these games :D
I am trying nano war right now tho, I do like that game! Hmmm... if(GalCon + NanoWar
Your answer
Follow this Question
Related Questions
Unity networking tutorial? 6 Answers
UNET AddPlayerForConnection with already existing player GameObject 0 Answers
Multiplayer objects isn't equals. 1 Answer
Can I add a network prefab at runtime? 1 Answer
how to sync a struct of numbers 0 Answers