I am making a character movement controller for a game where a player can transform into another game object by pressing the space bar. I have been able to give the 2 game objects the same position, but I am not sure how I can give them the same x and y velocity.
I have tried referencing the opposite gameObject's rigidbody, then setting it to whatever the current velocity of the active gameObjects is, but this does not work. The velocity does not transfer over. The two gameObjects that I am switching between are a player, and a ball.
Here is the code for the player (default)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerScript : MonoBehaviour
{
public GameObject ball1;
public GameObject player1;
public Rigidbody2D ballRB2D;
public Rigidbody2D rb2d;
public float speed;
public float jump;
float moveVelocity;
public bool grounded = true;
void OnTriggerEnter2D()
{
grounded = true;
}
private void OnTriggerStay()
{
grounded = true;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
ball1.transform.position = new Vector2(transform.position.x, transform.position.y);
ballRB2D.velocity = new Vector2(rb2d.velocity.x, rb2d.velocity.y);
ball1.SetActive(true);
player1.SetActive(false);
}
if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow))
{
if (grounded)
{
rb2d.velocity = new Vector2(rb2d.velocity.x, jump);
grounded = false;
}
}
moveVelocity = 0;
//Left Right Movement
if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
{
moveVelocity = -speed;
}
if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
{
moveVelocity = speed;
}
rb2d.velocity = new Vector2(moveVelocity, rb2d.velocity.y);
}
}
And here is the code for the ball (other gameObject)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallScript : MonoBehaviour
{
public Rigidbody2D rb2d;
public float moveSpeed = 5;
public GameObject player;
public GameObject ball;
public Rigidbody2D playerRB2D;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
player.transform.position = new Vector2(transform.position.x, transform.position.y);
playerRB2D.velocity = new Vector2(rb2d.velocity.x, rb2d.velocity.y);
player.SetActive(true);
ball.SetActive(false);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
rb2d.velocity = new Vector2(-moveSpeed , rb2d.velocity.y);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rb2d.velocity = new Vector2(moveSpeed, rb2d.velocity.y);
}
}
}
It makes no sense to handle the space click in both components! I suspect you are basically switching and then immediately switching back since the other component is doing the same thing in the other direction ... I would rather set this on a central controller which is an object that remains active and it rather controls the other two objects .. also your two movement inputs do not match which might cause very annoying UX ;)
So I would rather have something like e.g. (attached to your two controlled objects)
// Attached to each of your player/ball objects
public class Character : MonoBehaviour
{
// reference this objects rigidbody via Inspector
[SerializeField] private Rigidbody2D _rigidbody;
// public read-only access (encapsulation)
public Rigidbody2D Rigidbody => _rigidbody;
// Event others can attach listeners to
public event Action onGround;
private void Awake()
{
// Get on runtime as fallback
OnValidate();
}
private void Reset()
{
OnValidate();
}
private void OnValidate()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidody2D>();
}
private void OnTriggerEnter2D()
{
// invoke event if someone is listening
onGround?.Invoke();
}
private void OnTriggerStay()
{
onGround?.Invoke();
}
}
and then (attached to a central controller object, not a specific controlled one)
public class MovementAndSwitchController : MonoBehaviour
{
// reference the two (all) available characters via Inspector
[SerializeField] private Character[] _availableCharacters;
// index of the current active character
private int _currentCharacter;
private bool _grounded;
private void Start()
{
// Initially actuvate and listen to first character in array
_availableCharacters[0].gameObject.SetActive(true);
// just in case remove the listener first -> ensures it is always listening only once
_availableCharacters[0].onGround -= HandleGrounded;
_availableCharacters[0].onGround += HandleGrounded;
// deactivate all other characters
for(var i = 1; i < _availableCharacters.Length; i++)
{
_availableCharacters[i].gameObjet.SetActive(false);
}
}
private void Update()
{
var current = _availableCharacters[_currentCharacter];
// Handle character switch
if (Input.GetKeyDown(KeyCode.Space))
{
// increase index with wrap around at the end
_currentCharacter = (_currentCharacter + 1) % _availableCharacters.Length;
var next = _availableCharacters[_currentCharacter];
// copy over position and velocity
// note that Vector2 is a struct an therefore you don't need to use "new" here
next.Rigidbody.position = current.Rigidbody.position;
next.Rigidbody.velocity = current.Rigidbody.velocity;
// switch the active state
current.gameObject.SetActive(false);
next.gameObject.SetActive(true);
// stop listening to the previous character
current.onGround -= HandleGrounded;
// start listening to the new character
next.onGround -= HandleGrounded;
next.onGround += HandleGrounded;
// and finally switch them also in the local variable to be further handled below
current = next;
}
// Handle movement input
var velocity = current.Rigidbody.velocity;
if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
{
velocity.x = -speed;
}
else if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
{
velocity.x = speed;
}
if (_grounded && (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)))
{
velocity.y = jump;
_grounded = false;
}
current.Rigidbody.velocity = velocity;
}
private void HandleGrounded()
{
_grounded = true;
}
}