I am trying to make a script that Instantiates a ball when called. However the balls RigidBody2D and Collider2D are already in the scene upon build, prior to Instantiation. Why is this happening?
Ball Script:
public class Ball : MonoBehaviour
{
[SerializeField]
private Rigidbody2D rb;
[SerializeField]
private float speed = 10;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
Launch();
this.gameObject.name = "Ball";
BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>();
Rigidbody2D gameObjectsRigidBody = this.gameObject.AddComponent<Rigidbody2D>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void Launch()
{
//code used to determine in which direction the ball should go when it spawns
float x = Random.Range(0, 2) == 0 ? -1 : 1;
float y = Random.Range(0, 2) == 0 ? -1 : 1;
rb.velocity = new Vector2(speed * x, speed * y);
}
}
Thanks in advance!
The Awake function will be called even if the program is disabled. Therefore the RigidBody2D and Collider2D were already instantiated and were not updated. You should move the code from Awake to Start and enable the script using the snippet below:
private Scriptname script;
private void Awake()
{
script = GetComponent<ScriptName>();
script.enabled = true;
}
More info on this subject here
Related
I'm trying to solve an issue in Unity 2d game creation.
So, my issue is when the subject is idle, the 2d player object should be idle and when I move the character, it must animate like walking.
But in my case, the walking animation is not working but the character is moving.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float jumpmovement = 11f;
[SerializeField]
private float movement = 10f;
private float movementx;
[SerializeField]
private Rigidbody2D mybody;
private Animator anim;
[SerializeField]
private string Walk_Ani = "Player is walking";
private SpriteRenderer sr;
private void Awake()
{
mybody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Playerkeymove();
animateplayer1();
}
void Playerkeymove()
{
movementx = Input.GetAxisRaw("Horizontal");
// Debug.Log(movementx);
transform.position += new Vector3(movementx, 0f, 0f) * movement * Time.deltaTime;
// Debug.Log(transform.position);
}
void animateplayer1()
{
// anim.SetBool(Walk_Ani , true);
// we are going to the right side
if (movementx > 0)
{
anim.SetBool(Walk_Ani, true);
sr.flipX = false;
}
else if (movementx < 0)
{
// we are going to the left side
anim.SetBool(Walk_Ani, true);
sr.flipX = true;
}
else
{
anim.SetBool(Walk_Ani, false);
}
}
}
I can't understand, why I have a warning issue with boolean part where I guess that's where my animation takes part.
You are trying to set a boolean parameter in the Animator that doesn't exists. The code is correct. Check what name you choose for the bool parameter to active correctly the animation.
I'm trying to make the value in the text not to flickering when the player is moving but not sure ho to do it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerSpeedDistanceController : MonoBehaviour
{
public GameObject target;
public GameObject speedArea;
public float speed;
public Animator animator;
public Text movementSpeedUiText;
public Text distanceFormTargetUiText;
public Text playerDirectionUiText;
private float distance;
private Rigidbody rigidbody;
private Vector3 lastPosition;
private bool isMoving;
// Start is called before the first frame update
void Start()
{
lastPosition = transform.position;
isMoving = false;
rigidbody = GetComponent<Rigidbody>();
movementSpeedUiText.text = animator.GetFloat("Forward").ToString();
distanceFormTargetUiText.text = Vector3.Distance(transform.position, target.transform.position).ToString();
playerDirectionUiText.text = transform.forward.ToString();
}
// Update is called once per frame
void Update()
{
if (isMoving == false)
{
movementSpeedUiText.text = "0";
}
else
{
movementSpeedUiText.text = animator.GetFloat("Forward").ToString();
}
distanceFormTargetUiText.text = Vector3.Distance(transform.position, target.transform.position).ToString();
playerDirectionUiText.text = transform.forward.ToString();
if (transform.position != lastPosition)
isMoving = true;
else
isMoving = false;
lastPosition = transform.position;
}
}
It's flickering on this line when the player is moving :
movementSpeedUiText.text = animator.GetFloat("Forward").ToString();
It didn't flicker before but since I added the movement check in the Update it's start flickering.
What is flickering is the value in the text.
I tried to change the Update to LateUpdate but it didn't fix the flickering only when changed it to FixedUpdate then it's working but is it a good idea to put it all in FixedUpdate ? And I see now that sometimes the value in the text is minus negative why and what should I do ?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerSpeedDistanceController : MonoBehaviour
{
public GameObject target;
public GameObject speedArea;
public float speed;
public Animator animator;
public Text movementSpeedUiText;
public Text distanceFormTargetUiText;
public Text playerDirectionUiText;
private float distance;
private Rigidbody rigidbody;
private Vector3 lastPosition;
private bool isMoving;
// Start is called before the first frame update
void Start()
{
lastPosition = transform.position;
isMoving = false;
rigidbody = GetComponent<Rigidbody>();
movementSpeedUiText.text = animator.GetFloat("Forward").ToString();
distanceFormTargetUiText.text = Vector3.Distance(transform.position, target.transform.position).ToString();
playerDirectionUiText.text = transform.forward.ToString();
}
// Update is called once per frame
void FixedUpdate()
{
if (isMoving == false)
{
movementSpeedUiText.text = "0";
}
else
{
movementSpeedUiText.text = animator.GetFloat("Forward").ToString("F3");
}
distanceFormTargetUiText.text = Vector3.Distance(transform.position, target.transform.position).ToString();
playerDirectionUiText.text = transform.forward.ToString();
if (transform.position != lastPosition)
isMoving = true;
else
isMoving = false;
lastPosition = transform.position;
}
}
You are using a Rigidbody to move your object. The Physics system works together with the FixedUpdate which is executed in (as the name suggests) fixed time intervals - by default every 0.02 seconds.
So what happens if you put your code in Update might look somewhat like
u u u u u u u u
f f f f f
where u stands for Update call and f stands for FixedUpdate call.
So since the Physics are applied a bit desync to Update if hard check the values of transform.position it might just happen that your object is not yet moved or stoped by the Physics system.
So yes, either put your stuff in FixedUpdate - nothing speaks against it.
Or instead of transform.position rather use rigidbody.position
Rigidbody.position allows you to get and set the position of a Rigidbody using the physics engine.
which is the correct position of the object in the physics engine, also between FixedUpdate calls.
Or actually there is a way better method for getting the current movement of a Rigidbody which doesn't require storing and comparing any previous frame values:
Rigidbody.velocity
The velocity vector of the rigidbody. It represents the rate of change of Rigidbody position.
So you can shrink down your code to
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerSpeedDistanceController : MonoBehaviour
{
public GameObject target;
public GameObject speedArea;
public float speed;
public Text movementSpeedUiText;
public Text distanceFormTargetUiText;
public Text playerDirectionUiText;
private float distance;
private Rigidbody rigidbody;
// Start is called before the first frame update
void Start()
{
rigidbody = GetComponent<Rigidbody>();
UpdateDisplay();
}
// Update is called once per frame
void FixedUpdate()
{
UpdateDisplay();
}
private UpdateDisplay()
{
movementSpeedUiText.text = rigidbody.velocity.magnitude.ToString("0.###");
distanceFormTargetUiText.text = Vector3.Distance(rigidbody.position, target.transform.position).ToString();
playerDirectionUiText.text = transform.forward.ToString();
}
}
I'm having an issue instantiating Prefabs in Unity.
I'm working on a game where you have enemies moving towards you and you have to kill them. My original enemy game object moved towards the player with little to no problem, but the instantiations of that object wouldn't move.
To make matters more confusing, when I copied the game object and added it to the scene without instantiation, both game objects would move towards the player just fine.
Enemy script:
public class EnemieController : MonoBehaviour
{
[SerializeField]
float moveSpeed = 1;
[SerializeField]
private Rigidbody2D rb;
public Transform player;
public float health = 50;
void Start()
{
rb = GetComponent<Rigidbody2D>();
Debug.Log(player);
}
// Update is called once per frame
void Update()
{
MoveTowardsPlayer();
}
void MoveTowardsPlayer()
{
Vector3 mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = (player.position - transform.position).normalized;
rb.velocity = new Vector2(direction.x * moveSpeed, direction.y * (moveSpeed * 1));
}
}
Instantiation Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//using System;
[System.Serializable]
public class GameManagement : MonoBehaviour
{
public GameObject circleEnemie;
public Transform player;
[SerializeField]
float moveSpeed = 1;
// Start is called before the first frame update
void Start()
{
//Get random position to spawn ball
Vector3 screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(Random.Range(0, Screen.width), Random.Range(0, Screen.height), Camera.main.farClipPlane / 2));
GameObject enemie = Instantiate(circleEnemie, screenPosition, Quaternion.identity) as GameObject;
enemie.name = "enemiecircle";
enemie.tag = "Enemie";
}
// Update is called once per frame
void Update()
{
}
}
And if wanted, here are the enemies inspector specifications
Inspector Specifications
Sorry about the link, my reputation points are yet to reach 10 so I can't post images directly.
My guess would be that the Player referenced in your enemy prefab is a prefab itself that never moves.
You should make the prefab field itself of type EnemyController. This makes sure you only can reference a prefab here that actually has an EnemyController attached.
Then after Instantiate you can pass in the player reference of the GameManagement script like
public class GameManagement : MonoBehaviour
{
// Give this field the correct type
public EnemyController circleEnemie;
public Transform player;
[SerializeField]
float moveSpeed = 1;
// Start is called before the first frame update
void Start()
{
//Get random position to spawn ball
Vector3 screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(Random.Range(0, Screen.width), Random.Range(0, Screen.height), Camera.main.farClipPlane / 2));
// Instantiate returns the same type as the given prefab
EnemyController enemy = Instantiate(circleEnemie, screenPosition, Quaternion.identity);
enemy.name = "enemiecircle";
enemy.gameObject.tag = "Enemie";
// Now pass in the player reference
enemy.player = player;
}
// NOTE: When not needed better remove Unity message methods
// It would just cause overhead
//void Update()
//{
//}
}
Sidenote: In your EnemyController in MoveTowardsPlayer what do you need this for?
Vector3 mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Then whenever dealing with Rigidbody do the things in FixedUpdate otherwise it might break the physics and collision detection!
Then also don't use the Transform values but again the Rigidbody
private void FixedUpdate ()
{
MoveTowardsPlayer();
}
private void MoveTowardsPlayer ()
{
var direction = ((Vector2)(player.position - rb.position)). normalized;
rb.velocity = direction * moveSpeed;
}
I have a script that spawns my ground prefabs infinitely. The only problem is that occasionally there is a "bump" for the player to hit at the boundary of a new ground prefab.
It seems like one prefab is slightly higher than the previous one and when the player hits that small bump they go flying. How do I fix this?
I might just make the player a game object instead of a rigidbody and animate it instead of using actual physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundManager : MonoBehaviour
{
//oH is object height
public GameObject[] ground;
public GameObject[] obstacles;
private Transform playerTransform;
private float spawnZ = 0.0f;
private float spawnO = 0.0f;
public float oH;
private float tileLength = 40.0f;
private float oLength = 36.0f;
private int tilesOnScreen = 8;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < tilesOnScreen; i++)
{
SpawnTile();
SpawnObstacle();
}
}
// Player must be tagged "Player"
void Update()
{
if (playerTransform.position.z > (spawnZ - tilesOnScreen * tileLength))
{
SpawnTile();
SpawnObstacle();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
go = Instantiate(ground[0]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private void SpawnObstacle()
{
GameObject go;
go = Instantiate(obstacles[Random.Range(0, obstacles.Length)]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = new Vector3(0, oH, 1 * spawnO);
spawnO += oLength;
}
}
This code works to infinitely spawn the ground objects but has the bumps that I described. Just one bump is enough to screw up the whole game.
I'm a beginner at Unity with a problem for which I've not been able to find an answer on any of the boards. Creating a very basic Unity C# script, I have the following lines of code in my Awake() function:
Assert.IsNotNull(sfxJump);
Assert.IsNotNull(sfxDeath);
Assert.IsNotNull(sfxCoin);
The third assertion "
Assert.IsNotNull(sfxCoin) throws as null, even though the coin AudioClip is set in the Inspector:
Inspector script values:
However -- and this is the part that has me baffled -- for some reason sfxCoin is not null when invoked in the same script from an OnCollisionEnter() routine
So it appears Unity does register the object with the code -- eventually -- but the assertions fail with the initial Awake(), Start(), and Update() methods.
And this only is happening with sfxCoin. sfxJump and sfxDeath do not have this issue.
Any help would be appreciated
Entire script is below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class Player : MonoBehaviour
{
[SerializeField] private float jumpForce = 100f;
[SerializeField] private float forwardMomentum = 5f;
[SerializeField] private AudioClip sfxJump;
[SerializeField] private AudioClip sfxDeath;
[SerializeField] private AudioClip sfxCoin;
private Animator anim;
private Rigidbody Rigidbody;
private bool jump = false;
private AudioSource audioSource;
private void Awake()
{
Assert.IsNotNull(sfxJump);
Assert.IsNotNull(sfxDeath);
Assert.IsNotNull(sfxCoin);
}
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
Rigidbody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (!GameManager.instance.GameOver() && GameManager.instance.GameStarted())
{
if (Input.GetMouseButton(0))
{
GameManager.instance.PlayerStartedGame();
anim.Play("Jump");
audioSource.PlayOneShot(sfxJump);
Rigidbody.useGravity = true;
jump = true;
}
}
}
private void FixedUpdate()
{
if (jump)
{
jump = false;
Rigidbody.velocity = new Vector2(0, 0);
Rigidbody.AddForce(new Vector2(forwardMomentum, jumpForce), ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
switch (collision.gameObject.tag)
{
case "obstacle":
Rigidbody.AddForce(new Vector2(-50, 20), ForceMode.Impulse);
Rigidbody.detectCollisions = false;
audioSource.PlayOneShot(sfxDeath);
GameManager.instance.PlayerCollided();
break;
case "coin":
audioSource.PlayOneShot(sfxCoin);
GameManager.instance.Score(1);
print("GOT COIN");
break;
}
}
}
sorry, I found out what the problem was.
There was a second instance of a game object that was also using the same script that did not have sfxCoin set. It was hidden under a node in the hierarchy so I didn't see it.
Like I said, I'm a beginner at this.