Unity2D, can't sync my sprite across all clients (multiplayer) - c#

I'm trying to learn about Unet, but I'm having trouble getting the hang of it, despite following a few tutorials.
I am making a 2d game, and what I'm currently stuck at, is updating the way my characters sprite is turning (left or right).
My player prefab has: Sprite renderer, rigidbody2D, Network Identity(set to local player authority) and Network Transform.
This is the code attached to each player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class CharController : NetworkBehaviour {
public float maxSpeed = 1f;
[SyncVar]
bool facingRight = true;
Animator anim;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
}
void Moving()
{
float move = Input.GetAxis("Horizontal");
GetComponent<Rigidbody2D>().velocity = new Vector2(move * maxSpeed, GetComponent<Rigidbody2D>().velocity.y);
if(move > 0 && !facingRight)
{
RpcFlip();
}
else if(move < 0 && facingRight)
{
RpcFlip();
}
anim.SetFloat("MovingSpeed", Mathf.Abs(move));
}
private void FixedUpdate()
{
if (!isLocalPlayer)
{
return;
}
// handle input here...
Moving();
}
[ClientRpc]
void RpcFlip()
{
if (isLocalPlayer)
{
//facingRight = !facingRight;
//currentSprite.flipX = !currentSprite.flipX;
}
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
I know it's most likely something super simple I'm doing wrong, which just adds more pain to asking this, but any help is greatly appreciated! Thanks
Edit: The way my game is setup, one of the players is both a host and a client, I don't know if this makes it harder, but that is just the way unity does it, when playing locally.

I figured it out after reading the first 5 pages on google, and here is my final code, in case anyone else with the same problem stumbles in here:
public float maxSpeed = 1f;
Animator anim;
[SyncVar(hook = "FacingCallback")] //Everytime the bool netFacingRight is called, the method FacingCallback is called aswell.
//Since the bool only changes when CmdFlipSprite is called, it makes sure that CmdFlipSprite calls
//the change on our server, and FacingCallback calls it locally.
public bool netFacingRight = true;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
}
[Command]
public void CmdFlipSprite(bool facing)
{
netFacingRight = facing;
if (netFacingRight)
{
Vector3 SpriteScale = transform.localScale;
SpriteScale.x = 1;
transform.localScale = SpriteScale;
}
else
{
Vector3 SpriteScale = transform.localScale;
SpriteScale.x = -1;
transform.localScale = SpriteScale;
}
}
void FacingCallback(bool facing)
{
netFacingRight = facing;
if (netFacingRight)
{
Vector3 SpriteScale = transform.localScale;
SpriteScale.x = 1;
transform.localScale = SpriteScale;
}
else
{
Vector3 SpriteScale = transform.localScale;
SpriteScale.x = -1;
transform.localScale = SpriteScale;
}
}
////////
// Update is called once per frame
void Update () {
if (!isLocalPlayer)
{
return;
}
float move = Input.GetAxis("Horizontal");
GetComponent<Rigidbody2D>().velocity = new Vector2(move * maxSpeed, GetComponent<Rigidbody2D>().velocity.y);
if ((move > 0 && !netFacingRight) || (move < 0 && netFacingRight))
{
netFacingRight = !netFacingRight;
CmdFlipSprite(netFacingRight);
anim.SetFloat("MovingSpeed", Mathf.Abs(move));
}
}
You aparently have to call the function both locally and on the server!

Related

Unity3D go down on slopes with a animated character

I'm developping a 3D game Unity with a squirrel as the player.
I'm struggling with a problem of slopes. I know, there are a bunch of tutorial to go down a slope whithout 'floating in the air while walking' but I didn't find a fine solution. I think it's because of the horizontal animations of the squirrel (maybe). I have tried with addForce, with a modified speed, with gravity... (maybe I implemented it wrong). I know I can check if I'm in the air or not with CharacterController.isGrounded but I can't force the squirrel to stick on the slope while running or walking. I'm sorry by advance if my question is too vague or simple.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
public class Squirrel : MonoBehaviour {
Animator squirrel;
public float gravity = 1.0f;
private Vector3 moveDirection = Vector3.zero;
float axisH, axisV;
public static int munitions = 0;
Rigidbody rb;
[SerializeField]
float walkSpeed = 2f, runSpeed = 8f, rotSpeed = 100f, jumpForce = 350;
private bool isJumpKeyDown = false;
[SerializeField] bool isJumping = false;
Animator characterAnimator;
int JumpCount = 0;
public int MaxJumps = 1; //Maximum amount of jumps (i.e. 2 for double jumps)
[SerializeField] GameObject nb_munitions;
CharacterController characterController;
// Use this for initialization
void Start () {
munitions = 0;
squirrel = GetComponent<Animator>();
rb = GetComponentInChildren<Rigidbody>();
characterAnimator = GetComponent<Animator>();
JumpCount = MaxJumps;
characterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
GetInput();
nb_munitions.GetComponent<Text>().text = "Glands : " + munitions; //Affichage du score
Move();
}
private void FixedUpdate()
{
if (isJumpKeyDown)
{
squirrel.SetTrigger("jump");
JumpCount -= 1;
isJumpKeyDown = false;
}
}
public void GetInput()
{
axisV = Input.GetAxis("Vertical");
axisH = Input.GetAxis("Horizontal");
}
private void Move()
{
if (characterController.isGrounded)
{
//On the ground
}
else
{
//on the air
}
if (axisV > 0)
{
if (Input.GetKeyDown(KeyCode.LeftControl))
{
transform.position += Vector3.forward * walkSpeed * Time.deltaTime;
squirrel.SetBool("walk", true);
}
else
{
transform.position += Vector3.forward * runSpeed * Time.deltaTime;
squirrel.SetFloat("run", axisV);
squirrel.SetBool("walk", false);
}
}
else
{
squirrel.SetFloat("run", 0);
}
if (axisH != 0 && axisV == 0)
{
squirrel.SetFloat("h", axisH);
}
else
{
squirrel.SetFloat("h", 0);
}
if (axisH != 0)
{
transform.Rotate(Vector3.up * rotSpeed * Time.deltaTime * axisH);
}
if (Input.GetKeyDown(KeyCode.Space))
{
if (JumpCount > 0)
{
isJumpKeyDown = true;
}
}
//Call munitions
if (Input.GetKeyDown(KeyCode.LeftShift))
{
if (Squirrel.munitions > 0)
{
SpawnerScript.Instance.NewSpawnRequest();
munitions--;
}
}
}
}
You can try to get the angle of the slope and make it the pitch for the mesh of the squirrel.
I finally found the problem. My C# script "overwrited" the CharacterController and did not allow the squirrel to go down the slopes. Make sure you follow a move script that "respects" the CharacterController (if you pick one or you'll be fighting windmills).

transform.position not working in Unity 3d

I'm really new to Unity 3d and I'm trying to make a respawn with my character. It seems that the answer is really easy but I cannot see why my code is not working. If this is a duplicate, let me know.
public Vector3 PointSpawn;
void Start()
{
PointSpawn = gameObject.transform.position;
}
void Update()
{
if (gameObject.transform.position.y < 10)
{
gameObject.transform.position = PointSpawn; // This doesn't work
// gameObject.transform.LookAt(PointSpawn); ---> This DOES work ok
}
}
Parallel Script
public float HorizontalMove;
public float VerticalMove;
private Vector3 playerInput;
public CharacterController player;
public float MoveSpeed;
private Vector3 movePlayer;
public float gravity = 9.8f;
public float fallVelocity;
public float JumpForce;
public bool DoubleJump = false;
public Camera mainCamera;
private Vector3 camForward;
private Vector3 camRight;
void Start()
{
player = GetComponent<CharacterController>();
}
void Update()
{
HorizontalMove = Input.GetAxis("Horizontal");
VerticalMove = Input.GetAxis("Vertical");
playerInput = new Vector3(HorizontalMove, 0, VerticalMove);
playerInput = Vector3.ClampMagnitude(playerInput, 1);
CamDirection();
movePlayer = playerInput.x * camRight + playerInput.z * camForward;
movePlayer = movePlayer * MoveSpeed;
player.transform.LookAt(player.transform.position + movePlayer);
setGravity();
PlayerSkills();
player.Move(movePlayer * Time.deltaTime );
}
void CamDirection()
{
camForward = mainCamera.transform.forward;
camRight = mainCamera.transform.right;
camForward.y = 0;
camRight.y = 0;
camForward = camForward.normalized;
camRight = camRight.normalized;
}
void PlayerSkills()
{
if (player.isGrounded && Input.GetButtonDown("Jump"))
{
fallVelocity = JumpForce;
movePlayer.y = fallVelocity;
DoubleJump = true;
}
else if (player.isGrounded == false && Input.GetButtonDown("Jump") && DoubleJump == true)
{
fallVelocity = JumpForce *2;
movePlayer.y = fallVelocity;
DoubleJump = false;
}
}
void setGravity()
{
if (player.isGrounded)
{
fallVelocity = -gravity * Time.deltaTime;
movePlayer.y = fallVelocity;
}
else
{
fallVelocity -= gravity * Time.deltaTime;
movePlayer.y = fallVelocity;
}
}
Thanks in advance!
Just so the answer to the question is not in the comments:
The original problem is that the assignment gameObject.transform.position = PointSpawn appeared to do nothing. As the line is written properly, the position of this gameObject, must have been getting overwritten elsewhere.
With the addition of OP's movement script, the position of the player was getting overwritten in the movement's Update function. As the other assignment was being done in Update, the call order was not guaranteed to work as intended. The fix is either to assure that the movement Update is run not the frame of the new position assignment or to move the conditional and the assignment to a function that always runs after Update regardless of script execution order, LateUpdate.

Can't flip sprite using Mobile Input

I am currently working on porting my original project from PC version to mobile, I need help flipping sprite whenever I move left using mobile input, and flip right when I move right. Please help, im losing my mind, i'm sure it's easy but im missing something. I cant find a place to put my Flip() function
My code right now for movement using mobile input.
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed;
private Rigidbody2D myRB;
private Vector2 moveAmount;
private Animator myAnim;
private bool facingRight = true;
public Joystick joystick;
private void Start()
{
myAnim = GetComponent<Animator>();
myRB = GetComponent<Rigidbody2D>();
}
private void Update()
{
Vector2 moveInput = new Vector2(joystick.Horizontal, joystick.Vertical);
moveAmount = moveInput.normalized * speed;
if (moveInput != Vector2.zero)
{
myAnim.SetBool("isRunning", true);
}
else
{
myAnim.SetBool("isRunning", false);
}
}
private void FixedUpdate()
{
myRB.MovePosition(myRB.position + moveAmount * Time.fixedDeltaTime);
}
void Flip()
{
facingRight = !facingRight;
Vector3 Scaler = transform.localScale;
Scaler.x *= -1;
transform.localScale = Scaler;
}
}
No idea if your Flip method will do as you expect but logic-wise you can just call Flip if the horizontal direction doesn't match the facing:
private void Update()
{
Vector2 moveInput = new Vector2(joystick.Horizontal, joystick.Vertical);
moveAmount = moveInput.normalized * speed;
if (moveInput != Vector2.zero)
{
myAnim.SetBool("isRunning", true);
}
else
{
myAnim.SetBool("isRunning", false);
}
if ( (facingRight && moveAmount.x < 0)
|| (!facingRight && moveAmount.x > 0))
{
Flip();
}
}
i shall refer you to an answer that seems to be adequate for your question:
https://answers.unity.com/questions/952558/how-to-flip-sprite-horizontally-in-unity-2d.html
In code, you would just assign true/false to SpriteRenderer.flipX and .flipY.
Answer from JoeStrout
I think calling Flip() in update and passing horizontal value should work because it ranges from -1 to 1.

Unity C# Animator Transitions

I'm trying to get my enemies run animation to play when he is moving, and switch back to idle when he has stopped. However my current code doesn't seem to be doing this and instead my enemy remains in the idle state constantly. I have checked that my variables are being set but they just don't seem to be getting filtered through to my animator to make the transitions. I also have an error which doesn't seem to stop the game from playing but pops up in the console. The error is Controller 'Pirate': Transition " in state 'Idle_Pirate' uses parameter 'walking' which is not compatible with condition type. I assume this is the culprit but after trying a few different suggestions from googling I am struggling to find a solution. This is the code from the script attached to my enemy. Apologies if it is a little crude I am still learning. Any help is greatly appreciated.
using UnityEngine;
using System.Collections;
public class AI : MonoBehaviour {
public float walkSpeed = 2.0f;
public float wallLeft = 0.0f;
public float wallRight = 2.0f;
float walkingDirection = 1.0f;
Vector3 walkAmount;
float timeCheck = 0.0f;
float walkCheck = 0.0f;
public float maxSpeed = 5f;
bool facingRight = true;
bool idle = true;
Animator anim;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
}
// Update is called once per frame
void FixedUpdate () {
}
void Update () {
if (timeCheck >= 2.0f) {
walkAmount.x = walkingDirection * walkSpeed * Time.deltaTime;
if (walkingDirection > 0.0f && transform.position.x >= wallRight) {
walkingDirection = -1.0f;
Flip ();
} else if (walkingDirection < 0.0f && transform.position.x <= wallLeft) {
walkingDirection = 1.0f;
Flip ();
}
walkCheck = walkCheck + Time.deltaTime;
idle = false;
}
if (walkCheck >= 2.0f) {
idle = true;
walkAmount.x = 0;
timeCheck = 0.0f;
walkCheck = 0.0f;
}
timeCheck = timeCheck + Time.deltaTime;
transform.Translate(walkAmount);
anim.SetBool ("walking", idle);
}
void Flip () {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Managed to figure it out myself anyway, turns out I was using my animation in my animator instead of my sprites, must of dragged the wrong thing at some point. Thanks to those who took the time to read anyway.

Unity Click on 1 object to trigger another object

Please keep in mind that I'm new to Unity. I have 2 script that I want to "combline" but when I try then it don't Work.
I have a Script (Name : RobotController). This script controls the movement of the player. Up/Jump, Down, Left and Right. (This Works with Keyboard keys only atm)
This script Works just fine but now I want to add the feature of touch keys for phone. With this I mean that if a person click on the "up-Arrow" the player shall jump.
The Up-Arrow is an object.
This is were I get my problem. I have created the Up-Arrow with a collider and a script.
Up-Arrow script:
public class NewJumpScript : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnMouseOver()
{
if ((Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) || (Input.GetMouseButtonDown(0)))
{
Debug.Log("test");
}
}
}
Here is the RobotController script´, with ground check and so on.
public class RobotController : MonoBehaviour {
//This will be our maximum speed as we will always be multiplying by 1
public float maxSpeed = 2f;
public GameObject player;
public GameObject sprite;
//a boolean value to represent whether we are facing left or not
bool facingLeft = true;
//a value to represent our Animator
Animator anim;
//to check ground and to have a jumpforce we can change in the editor
bool grounded = true;
public Transform groundCheck;
public float groundRadius = 1f;
public LayerMask whatIsGround;
public float jumpForce = 300f;
private bool isOnGround = false;
void OnCollisionEnter2D(Collision2D collision) {
isOnGround = true;
}
void OnCollisionExit2D(Collision2D collision) {
anim.SetBool ("Ground", grounded);
anim.SetFloat ("vSpeed", rigidbody2D.velocity.y);
isOnGround = false;
}
// Use this for initialization
void Start () {
player = GameObject.Find("player");
//set anim to our animator
anim = GetComponent <Animator>();
}
void FixedUpdate () {
//set our vSpeed
//set our grounded bool
grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
//set ground in our Animator to match grounded
anim.SetBool ("Ground", grounded);
float move = Input.GetAxis ("Horizontal");//Gives us of one if we are moving via the arrow keys
//move our Players rigidbody
rigidbody2D.velocity = new Vector3 (move * maxSpeed, rigidbody2D.velocity.y);
//set our speed
anim.SetFloat ("Speed",Mathf.Abs (move));
//if we are moving left but not facing left flip, and vice versa
if (move > 0 && !facingLeft) {
Flip ();
} else if (move < 0 && facingLeft) {
Flip ();
}
}
void Update(){
if ((isOnGround == true && Input.GetKeyDown (KeyCode.UpArrow)) || (isOnGround == true && Input.GetKeyDown (KeyCode.Space))) {
anim.SetBool("Ground",false);
rigidbody2D.AddForce (new Vector2 (0, jumpForce));
}
if (isOnGround == true && Input.GetKeyDown (KeyCode.DownArrow))
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, 0.2f, 0.2f);
}
if (isOnGround == true && Input.GetKeyUp (KeyCode.DownArrow))
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, 0.3f, 0.3f);
}
}
//flip if needed
void Flip(){
facingLeft = !facingLeft;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
What needs to happen is that when The "Up-arrow" in the game is clicked then The person shall jump on the same tearms as in the RobotController script.
I hope you understand my question.
Thanks for your time and help.
I posted this same answer to both of the questions, because they are so similar. You can easily modify the code to your needs.
This is only one way to do it. There are probably many more, but this is the best I have encountered so far.
On the QUI button, script like this is needed:
private Mover playerMover;
void Start()
{
playerMover = GameObject.Find("Character").GetComponent<Mover>();
}
void OnMouseOver()
{
if (Input.GetMouseButton(0))
{
Debug.Log("pressed");
playerMover.MoveButtonPressed();
}
}
Notice that find is only done ones in start function, because it is computationally heavy.
And on the Character gameobject, script component like this is needed:
using UnityEngine;
using System.Collections;
public class Mover : MonoBehaviour {
public void MoveButtonPressed()
{
lastPressedTime = Time.timeSinceLevelLoad;
}
public double moveTime = 0.1;
private double lastPressedTime = 0.0;
void Update()
{
if(lastPressedTime + moveTime > Time.timeSinceLevelLoad)
{
// Character is moving
rigidbody.velocity = new Vector3(1.0f, 0.0f, 0.0f);
}
else
{
rigidbody.velocity = new Vector3(0.0f, 0.0f, 0.0f);
}
}
}

Categories