Why are my programmatically instantiated Prefabs not firing mouse events? - unity3d

In my Unity project I'm instantiating 20 Prefabs. These prefabs have box colliders and an attached script that includes:
private void OnMouseEnter()
{
print($"Mouse Enter detected by: {this.name}");
}
However these events never fire. If you google this issue you'll find multiple posts about this going back more than a decade. Unfortunately none of those threads seem to include a solution. If I drag the Prefab into the scene the mouse events will work, but if I create them programmatically they do not. Can someone explain to me how I can get mouse events working on programmatically created prefabs?
EDIT:
Since someone asked for my instantiation code here it is:
foreach (var movie in movies.Select((value, index) => (value, index)))
{
float angle = movie.index * Mathf.PI * 2 / movies.Count;
float x = Mathf.Cos(angle) * radius;
float z = Mathf.Sin(angle) * radius;
Vector3 pos = transform.position + new Vector3(x, height, z);
float angleDegrees = -angle * Mathf.Rad2Deg;
Quaternion rot = Quaternion.Euler(0, angleDegrees, 0);
var pre = Instantiate(moviePrefab, pos, rot, carousel.transform);
pre.transform.LookAt(Vector3.zero);
}

You didn't give me many details but I made what you want to do and this is how.
I made two classes:
NewBehaviourScript - for instatiating prefabs
public class NewBehaviourScript : MonoBehaviour
{
public GameObject Prefab;
private void Start()
{
for (int i = 0; i < 10; i++)
{
Instantiate(Prefab, new Vector3(transform.position.x + i, transform.position.y, 0), Quaternion.identity);
}
}
}
PrefabScript - script for prefab
public class PrefabScript : MonoBehaviour
{
private void OnMouseDown()
{
Debug.Log("Hit");
}
}
I created empty object "InstatiateObjectsHelper" in scene with the NewBehaviourScript
Then I created a prefab by creating it in the hierarchy, added to it PrefabScript, added to it BoxCollider, and then dragged & dropped it into the assets window.
Pressed play and tested it and it is working how you want it to.

Related

How to control player camera on angled surface/independent of xyz axis in Unity?

An illustration of my issue/what I'm trying to achieve
I managed to have my player move around the inside surface of a cylinder using gravity but an issue comes up when trying to rotate the camera to look around.
First, there's a script that simulates gravity by pushing the player against the inside walls of the cylinder. This script also keeps the player upright by changing the "up" direction of the player to always be facing the center of the cylinder. I know this keeps my player from looking up so for now I'm just working on getting them to look left and right.
Second, when the player is on the bottom of the cylinder and parallel with the Y-axis I can look left and right without issue because the camera rotates using the x-axis (see circle on the left side of the image). However when the player moves around the side of the cylinder the camera is still trying to rotate based on the X-axis even though they are not aligned resulting in the camera not accurately rotating (see the circle on the right side of the image), and by the time the player is 90deg around the cylinder cannot rotate at all (thanks I think to the gravity keeping the player perpendicular to the sides of the cylinder), and at 180deg around the rotation is inverted.
I assume there are two possible solutions that I have not been able to successfully implement:
ignore the world xyz axis' and rotate relative to the player's xyz.
do some math to figure out the proper angles when you take into account the player's rotation around the cylinder and the angle of the current "left" direction.
The problem with the first solution is I have not been able to successfully rotate the player independent of the world xyz. I tried adding the Space.Self to the Rotate() method but no success. The problem with the second is math scares me and I've managed to avoid Quaternions & Euler angles so far so I'm not even sure how to begin figuring that out.
If anyone has had a similar issue I would greatly appreciate any insight or suggestions on how to figure it out.
Here's my code for controlling the play movement/camera direction and my code for the gravity:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
InputManager inputActions;
InputManager.PlayerMovementActions playerMovement;
public GravityAttractor GravityAttractor;
public float moveSpeed = 15;
public float jumpHeight = 10f;
public float sensitivityX = 1f;
public float sensitivityY = 1f;
float mouseX, mouseY;
Vector2 mouseInput;
private bool isJumping = false;
private Vector3 moveDir;
private Vector2 moveInput;
private Rigidbody rbody;
private void Awake()
{
inputActions = new InputManager();
playerMovement = inputActions.PlayerMovement;
playerMovement.Movement.performed += context => moveInput = context.ReadValue<Vector2>();
playerMovement.Jump.performed += ctx => Jump();
//playerMovement.MouseX.performed += context => mouseInput = context.ReadValue<Vector2>();
playerMovement.MouseX.performed += context => mouseInput.x = context.ReadValue<float>();
playerMovement.MouseY.performed += context => mouseInput.y = context.ReadValue<float>();
rbody = GetComponent<Rigidbody>();
isJumping = false;
}
private void Update()
{
moveDir = new Vector3(moveInput.x, 0, moveInput.y).normalized;
if (rbody.velocity.y == 0)
isJumping = false;
}
private void FixedUpdate()
{
rbody.MovePosition(rbody.position + transform.TransformDirection(moveDir) * moveSpeed * Time.deltaTime);
MouseLook(mouseInput);
}
void Jump()
{
//use brackeys method of checking for contact with ground
if(isJumping == false && rbody.velocity.y == 0)
{
isJumping = true;
rbody.velocity = transform.up * jumpHeight;
Debug.Log("player jumped");
}
}
void MouseLook(Vector2 mouseInput)
{
mouseX = mouseInput.x * sensitivityX;
mouseY = mouseInput.y * sensitivityY;
var upTransform = GravityAttractor.transform.position - transform.position;
Vector3 relativeLook = upTransform - transform.forward;
Vector3 qLook = transform.forward - transform.position;
transform.Rotate(transform.up * mouseX * Time.deltaTime, Space.Self);
//transform.Rotate(relativeLook.normalized);
//transform.rotation = Quaternion.LookRotation(qLook);
//transform.Rotate(relativeLook.normalized, mouseX);
}
private void OnEnable()
{
inputActions.Enable();
}
private void OnDisable()
{
inputActions.Enable();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GravityAttractor : MonoBehaviour
{
public float gravityMultiplier = -10f;
private float radius;
public float gravity;
public float distance;
private void Awake()
{
radius = transform.localScale.x/2;
}
public void Attract(Transform body)
{
Vector3 centerOfGravity = new Vector3(transform.position.x, transform.position.y, body.position.z);
distance = Vector3.Distance(centerOfGravity, body.position);
//gravity will match multiplier no matter the radius of cylinder
gravity = gravityMultiplier * (distance/radius);
Vector3 gravityUp = (centerOfGravity - body.position).normalized;
Vector3 bodyUp = body.up;
body.GetComponent<Rigidbody>().AddForce(gravityUp * gravity);
Quaternion targetRotation = Quaternion.FromToRotation(bodyUp, gravityUp) * body.rotation;
body.rotation = Quaternion.Slerp(body.rotation, targetRotation, 50 * Time.deltaTime);
}
}
Problem is that you are trying to Rotate in local space around transform.up, which is in world space.
try to use just Vector3.up, instead of transform.up.
Or you can transform any vector from world to local space with transform.InverseTransformDirection()
transform.Rotate(Vector3.up * mouseX * Time.deltaTime, Space.Self);
I don't know hierarchy of your GameObjects. In order for this to work, you shuld rotate Player GameObject to face up to center of cilinder. And camera should be child of Player GameObject

Unity, Stop Moving Player when he hits obstacle or bounds [duplicate]

I just started learning Unity. I tried to make a simple box move by using this script. The premise is, whenever someone presses 'w' the box moves forward.
public class PlayerMover : MonoBehaviour {
public float speed;
private Rigidbody rb;
public void Start () {
rb = GetComponent<Rigidbody>();
}
public void Update () {
bool w = Input.GetButton("w");
if (w) {
Vector3 move = new Vector3(0, 0, 1) * speed;
rb.MovePosition(move);
Debug.Log("Moved using w key");
}
}
}
Whenever I use this, the box doesn't move forward on a 'w' keypress. What is wrong with my code? I thought it might be the way I have my Vector 3 move set up so I tried replacing the z-axis with speed, but that didn't work. Could someone tell me where I am messing up?
You move Rigidbody with Rigidbody.MovePosition and rotate it with Rigidbody.MoveRotation if you want it to properly collide with Objects around it. Rigidbody should not be moved by their position, rotation or the Translate variables/function.
The "w" is not predefined like SherinBinu mentioned but that's not the only problem. If you define it and use KeyCode.W it still won't work. The object will move once and stop.
Change
Vector3 move = new Vector3(0, 0, 1) * speed;
rb.MovePosition(move);
to
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
This should do it:
public float speed;
private Rigidbody rb;
public void Start()
{
rb = GetComponent<Rigidbody>();
}
public void Update()
{
bool w = Input.GetKey(KeyCode.W);
if (w)
{
Vector3 tempVect = new Vector3(0, 0, 1);
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
}
}
Finally, I think you want to move your object with wasd key. If that's the case then use Input.GetAxisRaw or Input.GetAxis.
public void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Vector3 tempVect = new Vector3(h, 0, v);
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
}
"w" is not predefined unless you explicitly define it. Use KeyCode.W
Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMover : MonoBehaviour {
public float speed;
private Rigidbody rb;
void Start () {
rb = GetComponent<Rigidbody>();
}
void Update () {
bool w = Input.GetKey(KeyCode.W);
if (w) {
Vector3 move = new Vector3(0, 0, 1) * speed *Time.deltaTime;
rb.MovePosition(move);
Debug.Log("Moved using w key");
}
}
}
Use Input.GetKey(KeyCode.W) for getting input.
EDIT NOTE:
To move the object relative to its initial position use rb.MovePosition(transform.position+move) rather than rb.MovePosition(move)
Instead of making w a bool, you can use axis, also, in unity editor you should make it so the rigidbody movement is frozen
here is some code
void update()
{
rb.AddForce(Input.GetAxis("Horizontal"));
}
bool w = Input.GetKeyDown(KeyCode.W);

MissingComponentException: There is no 'SpriteRenderer' attached to the “Koopa Troopa” game object, but a script is trying to access it

Error as below:
MissingComponentException: There is no 'SpriteRenderer' attached to the "Gumbaa" game object, but a script is trying to access it. You probably need to add a SpriteRenderer to the game object "Gumbaa". Or your script needs to check if the component is attached before using it. UnityEngine.Renderer.get_bounds () (at /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/GraphicsBindings.gen.cs:1007) EnemyMovement.Start () (at Assets/Scripts/EnemyMovement.cs:21)
And my game also got automatically pause whenever I clicked play button.
When I tried to Add Component 'SpriteRenderer' to Gumbaa, I then received another error message as below: Can't add component 'SpriteRenderer' to Koopa Troopa because it conflicts with the existing 'MeshFilter' derived component!
Screenshot of inspector
using System.Collections;
using UnityEngine;
public class EnemyMovement : MonoBehaviour
{
public float speed = 2.7f;
public LayerMask EnemyMask;
Transform myTrans;
float myWidth, myHeight;
Rigidbody2D rb;
SpriteRenderer mySprite;
// Use this for initialization
void Start ()
{
myTrans = this.transform;
rb = this.gameObject.GetComponent<Rigidbody2D> ();
mySprite = this.gameObject.GetComponent<SpriteRenderer> ();
myWidth = mySprite.bounds.extents.x;
myHeight = mySprite.bounds.extents.y;
}
// Update is called once per frame
void FixedUpdate ()
{
Physics2D.IgnoreLayerCollision (8, 9);
Vector2 LineCastPos = (myTrans.position.toVector2() + myTrans.right.toVector2() * myWidth + Vector2.up * myHeight * 1.2f);
Debug.DrawLine (LineCastPos, LineCastPos + myTrans.right.toVector2 () * 1.2f);
bool isBlocked = Physics2D.Linecast (LineCastPos, LineCastPos + myTrans.right.toVector2 () * 1.2f, EnemyMask);
if (isBlocked)
{
Vector2 currRot = myTrans.eulerAngles;
currRot.y += 180;
myTrans.eulerAngles = currRot;
}
Vector2 myVel = rb.velocity;
myVel.x = myTrans.right.x * speed ;
rb.velocity = myVel;
}
}
The error is pretty clear:
On your Gumba object, you added the script EnemyMovement
In the script, you do
mySprite = this.gameObject.GetComponent<SpriteRenderer> ();
This means that the GameObject that has EnemyMovement must also have SpriteRenderer
In the UnityInspector, do AddComponent and choose SpriteRenderer
Knowing if you really need to use a sprite renderer here is up to you

Why does this script not work on imported models

I've created a movement script that checks to see if an object is grounded, and if so, moves the object based on user input. When I attach it to an object I create in Unity, it works. When I attach it to an object I created in Blender and then imported, nothing. No errors, the object just fails to respond. I can't figure out what is causing the imported object to not respond to input. Both objects have a rigidbody and a collider.
This is the code for movement:
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour {
public float speed = 10f;
public float rotationSpeed = 100f;
private float distToGround;
private Collider collider;
void Start() {
collider = GetComponent<Collider>();
distToGround = collider.bounds.extents.y;
}
void Update() {
MovePlayer();
}
void MovePlayer() {
float translation = Input.GetAxis("Vertical");
float rotation = Input.GetAxis("Horizontal");
if (translation != null && rotation != null) {
if (IsGrounded()) {
transform.Translate(0, 0, translation * speed * Time.deltaTime);
transform.Rotate(0, rotation * rotationSpeed * Time.deltaTime, 0);
}
}
}
bool IsGrounded() {
return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
}
}
I've made a video demonstrating this: https://www.youtube.com/watch?v=qatBF5Ov3Zo&feature=youtu.be
This is a complication caused by Blender left handed axis vs Unity's right handed axis. When I exported a rotated object from Blender, the script works on the object as expected. The solution is to make sure that the object you are exporting is facing the Z axis instead of the native Y axis.

unity changing weapons.prefab issues

So i'm trying to change weapons for my 2d top down space shooter. by weapon I just mean my bullet prefab so it shoots a different bullet which I can then add damage to etc.
here is the code I have. when I press number 2 it shoots my prefab clone in the hierarchy but its greyed out and nothing shows up in the game view. Below is my playerShoot code.
public class playerShoot : MonoBehaviour {
public Vector3 bulletOffset = new Vector3 (0, 0.5f, 0);
float cooldownTimer = 0;
public float fireDelay = 0.25f;
public GameObject bulletPrefab;
int bulletLayer;
public int currentWeapon;
public Transform[] weapons;
void Start () {
}
void Update () {
if (Input.GetKeyDown(KeyCode.Alpha1)){
ChangeWeapon(0);
}
if (Input.GetKeyDown(KeyCode.Alpha2)){
ChangeWeapon(1);
}
cooldownTimer -= Time.deltaTime;
if (Input.GetButton("Fire1") && cooldownTimer <= 0){
cooldownTimer = fireDelay;
Vector3 offset = transform.rotation * bulletOffset;
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
bulletGO.layer =gameObject.layer;
}
}
public void ChangeWeapon(int num){
currentWeapon = num;
for (int i = 0; i < weapons.Length; i++){
if (i ==num)
weapons[i].gameObject.SetActive(true);
else
weapons[i].gameObject.SetActive(false);
}
}
}
Keeping the rest of the code same, just change the following line
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
to
GameObject bulletGO = (GameObject)Instantiate(weapons[currentWeapon].gameObject, transform.position + offset, transform.rotation);
What the change does is use the transforms for the weapons in your weapon array, rather than using the bulletPrefab.
The problem occured because you were Instantiating a single prefab, which was the same as the Transform you used for the first element in your weapons array. So, when you call ChangeWeapon(1), the prefab would get deactivated. This reulted in inactive GameObjects being instantiated.
What I would suggest you do is to have two separate prefabs and spawn those accordingly.