I'm making a game in unity where the user drags to shoot a ball at some objects at a distance. So far I have this DragAndShoot script:
//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class DragAndShoot : MonoBehaviour
{
public Transform prefab;
private Vector3 mousePressDownPos;
private Vector3 mouseReleasePos;
private Rigidbody rb;
private bool isShoot;
void Start()
{
rb = GetComponent<Rigidbody>();
}
private void OnMouseDown()
{
mousePressDownPos = Input.mousePosition;
}
private void OnMouseUp()
{
mouseReleasePos = Input.mousePosition;
Shoot(mouseReleasePos-mousePressDownPos);
}
private float forceMultiplier = 3;
void Shoot(Vector3 Force)
{
if(isShoot)
return;
rb.AddForce(new Vector3(Force.y,Force.x,Force.z) * forceMultiplier);
isShoot = true;
createBall();
}
void createBall(){
Instantiate(prefab, GameObject.Find("SpawnPoint").transform.position, Quaternion.identity);
}
}
As you can see, I made the function createBall() in order to respawn a ball prefab at the position of the game object SpawnPoint. When I run the game, the first ball shoots fine. And another ball respawns.
Issue: when I shoot the second ball and it moves, one more ball seems to have appeared at the second ball somehow, and it moves as well. Not sure why this is happening and how to fix it - can someone pls help? Thanks.
The problem is that you need to Destroy() the game object you threw first. Since you are just bringing the objects back when click down again, here is what you should do:
Make it so that it destroys the old object. Because you just keep instantiating the object, then when you throw it again it throws the old one too. If you understand what I mean, then hopefully, you can turn this into what you want. (It wasn’t exactly clear what your game was; this is what I interpreted)
Related
I have a GameObject with a RobotMovment script on it that looks like this:
public class RobotMovement : MonoBehaviour {
private Transform target;
private float speed = 2f;
void Start() {
target = GameObject.FindGameObjectWithTag("Ball").transform;
}
void Update() {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
}
}
I then have another script placed on a empty game object called Ball that looks like this:
public class Ball : MonoBehaviour {
public GameObject ballPrefab;
void Start() {
var randomPosition = new Vector3(Random.Range(GetComponent<Collider>().bounds.min.x, GetComponent<Collider>().bounds.max.x), 0.25f, Random.Range(GetComponent<Collider>().bounds.min.z, GetComponent<Collider>().bounds.max.z));
Instantiate(ballPrefab, randomPosition, Quaternion.identity);
}
}
What I want to achieve is to have the RobotMovement gameobject move towards the ball prefab that gets spawned on the start of the game. Now I get a: nullreferenceexception object reference not set error on start of the game. It might be that the the ball prefabs position can't be read by the RobotMovement script at start?
The Awake() function executes before Start(). It's a good idea to use Awake() to find and assign references such as target, then use Start() for whatever initialisation logic you need once you're confident all your references are in place.
That said, your null reference could be in a number of places, but most likely:
No gameobject called "Ball" is found when RobotMovement calls Start().
Ball gets destroyed or the reference is lost at some point, and RobotMovement.Update() throws.
No collider component on Ball, so Ball.Start() throws.
One thing you didn't ask for, but is important to know, is that GetComponent() is an expensive method to invoke, so you'd be better off calling it just once:
Collider collider = GetComponent<Collider>()
var randomPosition = new Vector3(Random.Range(collider.bounds.min.x, collider.bounds.max.x), 0.25f, Random.Range(collider.bounds.min.z, collider.bounds.max.z));
I have a game like CubeSurfer. When player goes through an collectable, two methods are called (for "jumping" and creating a cube under player). But, when player goes through second, or third collectable, nothing happens. I tried to attached script to collectable, and tried to put the method "OnTriggerEnter" in the body of methods "Start" and "Update". But it doesn't work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCollider : MonoBehaviour
{
[SerializeField] Collider player;
[SerializeField] GameObject destroyIt;
[SerializeField] GameObject objToClone;
[SerializeField] Transform rootObjInScene;
Transform curParent;
Vector3 posOffset, posPlayer;
float distance = 1f;
private void OnTriggerStay(Collider player)
{
PlayerJumping();
CreateAnother();
Destroy(destroyIt);
}
public void PlayerJumping()
{
posPlayer = Vector3.up * distance;
Vector3 playerPos = rootObjInScene.position + posPlayer;
rootObjInScene.transform.position = playerPos;
}
public void Awake()
{
curParent = rootObjInScene;
posOffset = Vector3.down * distance;
}
public void CreateAnother()
{
Vector3 newPos = curParent.position + posOffset;
GameObject newObj = Instantiate(objToClone, newPos, Quaternion.identity, curParent);
curParent = newObj.transform;
}
}
You should also check your Rigibody collision detection mode in the inspector.
By default, this is set to Discrete, but if your object is fast-moving, you should set it to Continuous or ContinuousDynamic, otherwise the collisions won't be registered because the object is moving too quickly.
These are a little bit performance intensive, so I'd still recommreading up on the Unity documentation or digging through a couple of good articles to know what's best for you :)
You shouldn't call OnTriggerEnter explicitly (in your Update()), it's an event message and gets called automatically by unity when you enter a Collider marked isTrigger.
You can use OnTriggerEnter only if your collider is marked isTrigger, if you want to get a collision with a Collider that is not explicitly a trigger, you use OnCollisionEnter
To execute a method repeatedly while touching a Collider use OnCollisionStay, while touching or inside a Collider with isTrigger use OnTriggerStay
OnCollisionStay
OnTriggerStay
You can use OnCollisionStay or OnTriggerStay
void OnCollisionStay(Collision collisionInfo)
{
//Do something when collisionInfo.gameObject stay in this.gameObject collider
}
I have looked around the web for answers but can't find a working one.
I have tried changing the code and I am pretty sure it's a very easy noob mistake as this is my first game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class respawn : MonoBehaviour
{
[SerializeField] private Transform player;
[SerializeField] private Transform respawnPoint;
private Rigidbody Rigidbody;
void FixedUpdate()
{
if (player.position == respawnPoint.position);
{
GetComponent<Rigidbody>().velocity = 0;
Rigidbody.angularVelocity = 0;
}
}
private void OnTriggerEnter(Collider other)
{
player.transform.position = respawnPoint.transform.position;
}
}
The very first thing you want to do is to look at the Unity error console. Your code throws multiple errors, they will appear in red. Double-click on an error to be taken to its line in your code.
Also, try get a good editor, like VS Code, as it will help you find issues immediately by adding red wavy underlines. With above two approaches in hand, you will be able to find ~99% of your issues yourself the next time, and then can ask in forums for the remaining 1%.
Let's go through your code step by step:
private Rigidbody Rigidbody;
In Unity you normally want the type in upper case Rigidbody, but the variable name in lowercase rigidbody (even though the C# coding conventions suggest uppercase properties). Either way, don't use the same name as the type, so write:
private Rigidbody rigidbody;
or e.g. (I'm now omitting the default private, though you may also prefer to keep it, and I use a shorter variable name, and clarify its start value):
Rigidbody body = null;
In the following code:
GetComponent<Rigidbody>().velocity = 0;
... you first of all get the rigidbody component again. This isn't needed if you got it once in start (and for repeated usage, can lag), so do it like this:
void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
And secondly, you assign a 0, but you want to assign new Vector3(0f, 0f, 0f), or just
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
Lastly, you write
player.transform.position = respawnPoint.transform.position;
... but player and respawnPoint are already transforms, so you can use the simpler
player.position = respawnPoint.position;
Good luck!
When you enter the Trigger you will be teleported to the respawn position, but you cannot depend on FixedUpdate running on that exact pixel of the respawn position.
The most straigh forward answer is to, well, do the code as you would describe the issue: "When you enter a trigger, set the position to the respawn position and set velocity to 0":
I would change the entire script to:
public class respawn : MonoBehaviour
{
[SerializeField] private Transform player;
[SerializeField] private Transform respawnPoint;
private Rigidbody rigidbody;
void Start()
{
rigidbody = GetComponent<Rigidbody>()
}
private void OnTriggerEnter(Collider other)
{
player.position = respawnPoint.position;
rigidbody.velocity = Vector3.zero;
}
}
and don't forget to populate the player and respawnPoint values in the inspector.
Also, keep in mind that the current code is prone to errors, if you have other objects moving around. Right now, if ANY object enters the Trigger the PLAYER position will be reset. You could fix this with check for player
private void OnTriggerEnter(Collider other)
{
if (other.transform == player)
{
player.position = respawnPoint.position;
rigidbody.velocity = Vector3.zero;
}
}
So I have assigned a script to a simple ball and tried to attach this code:
private Rigidbody2D rb2D;
Vector2 speed;
void Start()
{
rb2D = gameObject.AddComponent<Rigidbody2D>();
speed = new Vector2(1,0);
}
void FixedUpdate()
{
rb2D.AddForce(speed);
}
And It doesn't work. May I know what I did wrong ? As it seems that addforce doesn't work. And the object with script attached has a rigidbody2d component
Here are the two Error messages I am receiving.
This seems to only happen when the Run animation is playing. Any help would be appreciated.
I am building this game for android and I am using mouse clicks to move the character. Since mouse clicks translate to touch events this should have no barring on the game as far as I know.
I guess I should also note that the animations play fine while playing the game.
'defaultModelfbx' AnimationEvent 'FootL' has no receiver! Are you missing a component?
'defaultModelfbx' AnimationEvent 'FootR' has no receiver! Are you missing a component?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerController : MonoBehaviour
{
float speed = 10;
float rotSpeed = 5;
Vector3 targetPosition;
Vector3 lookAtTarget;
Quaternion playerRot;
bool moving = false;
Animator thisAnim;
void Update()
{
thisAnim = GetComponent<Animator>();
// Get movement of the finger since last frame
if (Input.GetMouseButton(0))
{
SetTargetPosition();
}
if (moving)
{
Move();
}
}
void SetTargetPosition()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, 1000))
{
targetPosition = hit.point;
lookAtTarget = new Vector3(targetPosition.x - `transform.position.x, 0, targetPosition.z - transform.position.z);`
playerRot = Quaternion.LookRotation(lookAtTarget);
moving = true;
}
}
void Move()
{
thisAnim.SetFloat("speed", 1);
transform.rotation = Quaternion.Slerp(transform.rotation, playerRot, rotSpeed * Time.deltaTime);
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
if (transform.position == targetPosition)
{
moving = false;
thisAnim.SetFloat("speed", 0);
}
}
}
I had the same error, but with a different cause and solution.
All my code was in a (grand)parent gameobject (e.g. WerewolfPlayer) and I wanted to keep it this way. The animator was in a (grand)child gameobject (e.g. WEREWOLF_PBR) and it couldn't find the animation events:
To fix this, I bubbled-up the event from the child gameobject to the parent:
I edited the PlayerController.cs in the parent gameobject (e.g. WerewolfPlayer) to find the new "hand off animation events" script and fire the animation events when they happen:
using UnityEngine;
using System;
public class PlayerController : MonoBehaviour
{
private HandOffAnimationEvent handOffAnimationEvent;
// called when animation event fires
public void Pickup()
{
Debug.Log("player picks up object here...");
}
void OnEnable()
{
handOffAnimationEvent = GetComponentInChildren<HandOffAnimationEvent>();
handOffAnimationEvent.OnPickup += Pickup;
}
void OnDisable()
{
handOffAnimationEvent.OnPickup -= Pickup;
}
}
The new HandOffAnimationEvents.cs was added to the child gameobject (e.g. WEREWOLF_PBR) and when the animation event fires, it fires its own event:
using UnityEngine;
using System;
public class HandOffAnimationEvent : MonoBehaviour
{
public event Action OnPickup;
// This is the animation event, defined/called by animation
public void Pickup()
{
OnPickup?.Invoke();
}
}
Okay thanks to Nathalia Soragge I have found the solution to the problem.
If you look closely at the animation window you can see two white dashes at the top of the time line. I have clicked on one of them in the picture, and sure enough it is one of the events that were throwing the error message.
I am assuming I can just delete these two events since I do not have them anywhere in code. In this case it is looking for the events in my PlayerController since it is attached to my Model defaultModelfbx
UPDATE: I have deleted both events and now everything is running smoothly. Thanks again Nathalia!!!!!!! ;)
If you don't want to listen to any events, you can set Animator.fireEvents to false.
public Animator animator;
void Start()
{
animator.fireEvents = false;
}
https://docs.unity3d.com/ScriptReference/Animator-fireEvents.html
I had the same problem.I solved this by deleting all animation events and recreating all of them again.