Unity2D Tilemap Collision Issues - unity3d

I'm working on a game and using the new Tilemap features. They are working fantastic so far. So easy to whip up a level. However I am running into issues when reflecting a laser on the tilemap colliders. As you can see in the GIF below the first few lasers reflect fine, but the sometimes in the small crack where the colliders meet I am getting unwanted behaviour for my laser.
The laser is made up of a small pellet sprite renderer object with a trail renderer for the color behind and a circle collider.
Here is my collision code. Where direction is a class Vector2 variable and rb is theGameObject' s RigidBody2D component stashed in the Start() function.
if (collision.collider.gameObject.layer == LayerMask.NameToLayer ("Terrain")) {
direction = Vector2.Reflect (direction, collision.contacts[0].normal);
rb.velocity = direction.normalized*GameManager.instance.bouncySpeed;
}
What steps can I take to avoid this behaviour?

I think that the condition returns true since the laser is redirected. But some variable is wrong somewhere.
Try to print direction, collision.contacts[0].normal, collision.contacts.Length, GameManager.instance.bouncySpeed and rb.velocity.
if (collision.collider.gameObject.layer == LayerMask.NameToLayer ("Terrain")) {
direction = Vector2.Reflect (direction, collision.contacts[0].normal);
rb.velocity = direction.normalized*GameManager.instance.bouncySpeed;
Debug.Log("////////");
Debug.Log("direction: " + direction);
Debug.Log("collision.contacts[0].normal: " + collision.contacts[0].normal);
Debug.Log("collision.contacts.Length: " + collision.contacts.Length);
Debug.Log("GameManager.instance.bouncySpeed: " + GameManager.instance.bouncySpeed);
Debug.Log("rb.velocity: " + rb.velocity);
Debug.Log("////////");
}
Compare these values when working and when bugged. Post them here as well if you don't find what's wrong

Related

2D Object Not Detecting When Hit by Raycast

I'm creating a top down 2D game, where the player has to break down trees. I made it so the player casts a ray toward the mouse, and when the ray hits a tree, it should lower the tree's health. I don't get any errors when I run the game or click, but it seems like the tree isn't detecting the hits.
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.Raycast(playerRb.transform.position, mousePosition - playerRb.transform.position, 2.0f);
if (hit.collider != null)
{
if (hit.collider == GameObject.FindWithTag("Tree"))
{
hit.collider.GetComponent<TreeScript>().treeHealth--;
}
}
}
}
Still pretty new to coding and I'm teaching myself, so please make your answer easy to understand to help me learn.
Input.mousePosition is equal to the pixel your mouse is on. This is very different than the location your mouse is pointing at in the scene. To explain further, Input.mousePosition is where the mouse is. Think about it. If the camera was facing up, the mouse positon would be the same, but where they are clicking is different.
Instead of using Input.mousePosition, You should pass this into a function called Ray Camera.ScreenPointToRay();
You just input the mouse position and then use this new ray to do the raycast.
ANOTHER EXTREMELY IMPORTANT THING 1: Do not use Camera.main in Update(), as it uses a GetComponet call, which can cause perormance decreases. Store a reference of it in your script and use that.
Extremely important thing 2: I notice you are using GetComponent to change the tree's health. This is fine, but do not use GetComponent if you don't have to.
Like this:
Camera cam;
void Start()
{
cam = Camera.main; //it is fine to use this in start,
//because it is only being called once.
}
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray);
...
}
}
You need to convert your mouse position from screen point to world point with Z value same as the other 2D objects.
Vector3 Worldpos=Camera.main.ScreenToWorldPoint(mousePos);
Also use a Debug.DrawRay to check the Raycast
Debug.DrawRay(ray.origin, ray.direction*10000f,Color.red);
Source

Detect collisions between 2 objects

I'm trying to do a little game on mobile using Unity and I've got a problem with the rotation of a maze.
To add context :
When your moving your finger on the screen, the maze is rotating on himself. There is a ball in it and you need to make it go on a cell to win the level.
When the maze is rotating too fast, the ball falls down and go through the ground and I don't know how to fix it.
I tried to play with the gravity, colliders...
This is the same when the ball is jumping (when the maze is going up and down quickly).
For the moment I just reset the ball position when you're falling.
{
ball.transform.position = new Vector3(0, 2, 0);
maze.transform.position = Vector3.zero;
maze.transform.rotation = Quaternion.identity;
}
Do you guys have some ideas ? Thanks
I had a similar problem in a tilt maze mini-game I worked on. Ideally implementing jkimishere's solution will work but I assume the maze is moving too fast for the collisions to register properly. You'll need to smooth the maze's rotation with a Lerp. In our case we had pressure plates with a tilt value, so it doesn't directly translate to your mobile use but perhaps give you a nudge in the right direction. We used:
public GameObject maze;
private float _lerpTime;
private bool _isRotating;
private Quaternion _startingRot, _desiredRot;
private void Awake()
{
_startingRot = maze.transform.localRotation;
}
private void Update()
{
//Don't want to move the maze if we don't ask for it
if(!_isRotating)
return;
//Lerp the maze's rotation
_lerpTime = Mathf.Clamp(_lerpTime + Time.deltaTime * 0.5f, 0f, 1f);
maze.transform.localRotation = Quaternion.Lerp(_startingRot, _desiredRot, _lerpTime);
//Once the maze gets where it needs to be, stop moving it
if(affectedObject.transform.localRotation.Equals(_desiredRot)
_isRotating = false;
}
private void ActivateTilt()
{
//Set the new starting point of the rotation.
_startingRot = maze.transform.localRotation;
//However you want to calculate the desired rotation here
//Reset our lerp and start rotating again
_lerpTime = 0f;
_isRotating = true;
}
This will ease the rotation of your maze over time. So that the ball can adapt to the new collider positions.
In the rigidbody(for the ball), make the collision detection to continuous, and in the rigidbody for the maze(if you have one) set the collision detection to continuous dynamic. Hope this helps!
I think that is unavoidable if you allow the player to move the platform freely. I suggest you restrain the speed at wich the player can tilt the platform. This way, the ball will have more frames to adapt to the new slope

Unity 3D - Move Humanoid head muscles using script

Goal
I would like to rotate a head of a humanoid character given x, y, z rotation values using a script (without using any other object in the scene as "look" direction).
What I want to do
When setting-up the rigging of a Humanoid character (humanoid prefab --> Rig --> Animation Type: Humanoid --> Configure --> Muscles & Settings), you see the following interface: https://docs.unity3d.com/Manual/MuscleDefinitions.html
In this menu, under Head, you can drag a slider to move e.g. your humanoid head up and down. I want to achieve the same with a script, but I don't know how to do this.
Resources
This question never received a proper answer with example code: https://answers.unity.com/questions/824628/can-we-using-script-to-moving-the-muscles-in-unity.html
I presume I have to do something with HumanPose.muscles (https://docs.unity3d.com/ScriptReference/HumanPose-muscles.html), however with the lack of code samples, I have no idea how to approach this.
Edit 3: This link has a code sample for HumanPose, but I didn't get it to work yet: https://forum.unity.com/threads/humanposehandler.430354/
Question
How to get the head muscles of a humanoid character and rotate them by giving values through a script? (Or any other way how to rotate the head using head rotation values, without another object in the scene). Any help would be appreciated.
Edit 1 & 2: Sample code
I receive a JSON formatted message from which I extract the radian values and change it into degrees:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json.Linq; // JSON reader; https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347
public class HeadRotator : MonoBehaviour {
Quaternion rotation = Quaternion.identity;
// Radians to degree
float Rad2Degree = 180 / Mathf.PI;
// Human muscle stuff
HumanPoseHandler humanPoseHandler;
HumanPose humanPose;
Animator anim;
//Transform head;
// Use this for initialization
void Start () {
// get attached Animator controller
anim = GetComponent<Animator>();
//head = anim.GetBoneTransform(HumanBodyBones.Head);
//Debug.Log (head);
}
// Use JSON message to set head rotation and facial expressions;
// IEnumerator to run in main thread (needed for SetBlendShapeWeight)
public IEnumerator RequestHeadRotation(JObject head_pose)
{
// rotate head of character with received x, y, z rotations in radian
List<float> head_rotation = new List<float> ();
foreach (KeyValuePair<string, JToken> pair in head_pose) {
//Debug.Log(pair);
// store head rotation in degrees
head_rotation.Add(float.Parse(pair.Value.ToString())*Rad2Degree*10);
}
Debug.Log ("" + head_rotation[0] + ", " + head_rotation[1] + ", " + head_rotation[2]);
//Quaternion rotation = Quaternion.Euler(new Vector3(head_rotation[0], head_rotation[1], head_rotation[2]));
//Debug.Log ("" + rotation[0] + ", " + rotation[1] + ", " + rotation[2] + ", " + rotation[3]);
// head.Rotate (rotation);
// https://docs.unity3d.com/ScriptReference/Quaternion-eulerAngles.html
rotation.eulerAngles = new Vector3 (head_rotation[0], head_rotation[1], head_rotation[2]);
// Animator.GetBoneTransform()
anim.SetBoneLocalRotation(HumanBodyBones.Head, rotation);
yield return null;
}
}
Head rotation data: 3.208564, 0.4583662, 0.1145916
Quaternion data: 0.0280001, 0.003970424, 0.0008876149, 0.9995997
I don't really know well how to set either the head bone or the muscles. Most examples provide only a snippet of the code, so I'm struggling in figuring out how it works.
Edit 2: I feel I'm getting close, but anim.SetBoneLocalRotation(HumanBodyBones.Head, rotation); seems to get ignored.
Edit 4: I put on Github a simple version of my head rotation attempt: https://github.com/NumesSanguis/Basic-Unity-Head-Rotation
After testing for a while, I figured it out.
Firstly, set the animator to IK Pass.
Then use SetBoneLocalRotation inside OnAnimatorIK method. You can know more about this method here: https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnAnimatorIK.html Following is the modified code that worked for me
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HeadRotator : MonoBehaviour
{
public Vector3 rot = new Vector3(20, 30, 10);
Animator anim;
// Bone stuff
Transform head;
// !!! Head bone rotation approach !!!
void Start () {
// get attached Animator controller
anim = GetComponent<Animator>();
head = anim.GetBoneTransform(HumanBodyBones.Head);
}
void OnAnimatorIK (int layerIndex) {
print ("OnAnimatorIK - running");
anim.SetBoneLocalRotation(HumanBodyBones.Head, Quaternion.Euler(rot));
}
}
Thanks to #ZayedUpal head bone rotation is working, and thanks to a friend, setting muscle values is also working!
The full Unity 3D project for both options can be found here: https://github.com/NumesSanguis/Basic-Unity-Head-Rotation
Code snippet of muscle values approach:
// !!! Human Pose approach !!!
void Start () {
// https://forum.unity.com/threads/humanposehandler.430354/
// get attached Animator controller
anim = GetComponent<Animator>();
// run this if you want the indexes to muscles on your character
LookUpMuscleIndex();
// TODO keeping body above plane
//Vector3 current_position = transform.position;
// get human pose handler
humanPoseHandler = new HumanPoseHandler(anim.avatar, transform);
// get human pose
humanPose = new HumanPose();
// TODO keeping body above plane
//humanPose.bodyPosition = current_position;
// reference pose to pose handler
humanPoseHandler.GetHumanPose(ref humanPose);
// set a specific musle; 9: Neck Nod Down-Up
humanPose.muscles[9] = -20f;
Debug.Log(humanPose.muscles[9]);
// use pose information to actually set the pose;
humanPoseHandler.SetHumanPose(ref humanPose);
}
Two addition to the muscle answer
1, To solve the problem:
The character to which it was applied moved quite far from where he was supposed to be when humanPoseHandler.SetHumanPose(ref humanPose); was set.
You need to add:
humanPose.bodyPosition = transform.position;
humanPose.bodyRotation = transform.rotation;
That will keep your character exactly where it is located.
2, Actually the muscle solution is still valid when an AnimatorController is attached to current humanoid character. You just need to switch Update Mode to Animate Physics and put your muscle code in Update(). That will make your muscle code (which executes in Update()) executes after the AnimationController effects (which executes in FixedUpdate(), which is called earlier than the Update()).
This approach will allow you directly control some muscle, and use Unity IK effect at the same time.

Unity3D Raycasting with Oculus Rift Integration / Measuring the distance

I want to measure the distance between two points in Unity3D Game Engine using the Oculus Rift. The points are targeted by the user by looking at point A, pressing alpha1 on the keyboard and B, pressing alpha2 on the keyboard. I got this far:
#pragma strict
private var measuring = false;
private var startPoint : Vector3;
private var dist;
function Update() {
var hit : RaycastHit;
if (Input.GetKeyDown(KeyCode.Alpha1)) {
dist = 0.0f;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
measuring = true;
startPoint = hit.point;
}
}
if (measuring && Input.GetKeyDown(KeyCode.Alpha2)) {
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), hit)) {
// if (Physics.Raycast(transform.position, transform.forward, hit, 10)) {
dist = Vector3.Distance(startPoint, hit.point);
}
}
}
function OnGUI() {
if (measuring) {
GUI.Label(Rect(50,50,180,50), "Distance: " + dist.ToString());
}
}
My problem is, that this code only works with the standard main camera object, but I want to use the Oculus integrated OVRCameraRig. I get the following exception message:
NullReferenceException: Object reference not set to an instance of an object
MeasureInGame.Update () (at Assets/MeasureInGame.js:11)
I found a solution on this site: https://kaharri.com/unity-gaming-shootingaiming-part3-oculus/ I created a ShotSpawner object as a child of OVRCameraRig (this should act like a gun in front of the camera) and changed the Raycast to
Physics.Raycast(transform.position, transform.forward, hit, 10)
to get the users view. But it also doesn't seem to work.
How can I get the aiming done with the Oculus a Main Camera. And is it correct that I strictly need to have a collider on my objects to be measured or is there a solution without collider?
Greetings
First of all - are you really using the mouse with Oculus? Of course you can, but the standard way is to look at the selected object (cursor is in the center of viewport). Cast the ray from the "middle" eye in the oculus integration - it's the object that is the parent to the left and right eyes. Use this ray instead of the one from Camera.main.ScreenPointToRay:
// add a reference to the middleEyeGameobject
// in your class and link it in the inspector
var ray=new Ray(middleEyeGameobject.transform.position, middleEyeGameobject.transform.forward);
Also, you don't have to specify the distance with hit raycast (remove the fourth parameter to Physics.Raycast).
And for the additional question yes, everything has to have a collider with Physics.Raycast. And yes, there are other ways to do this, but the built in ones (2) still require colliders (or similar), although they can be virtual, not attached to objects.
It's best to use colliders, perhaps on their own layer.
The NullReferenceException you are getting is because you are using Camera.main.ScreenPointToRay(Input.mousePosition) , camera.main uses the MainCamera tag which is not set for your OVRCameraRig. You can set tag of this camera as mainCamera if OVRCameraRig is supposed to be your mainCamera or otherwise you can take reference of this camera (OVRCameraRig) and use cameraRef.ScreenPointToRay(Input.mousePosition). :)

raycast not showing hit

I have a raycast, however it goes through the enemies as it is suppose to but it hits nothing else. If I remove the mask it hits the enemies layer. If I remove the layer and use raycastall it hits only the enemies.
If I use raycast it goes clear through a wall and hits the player but does not show as a hit, in fact i get the error
NullReferenceException: Object reference not set to an instance of an object
EnemyAI.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/EnemyAI.cs:32)
Line 32 is Debug.Log (hit.transform.gameObject);. If I remove it, nothing happens at all. No error, and no hit.
Heres is the code
void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.tag == "Player") {
myStats.inRange = true;
Vector2 direction = other.transform.position - transform.position;
hit = Physics2D.Raycast(transform.position, direction, myCircle.radius + 1, LayerMask.GetMask("enemies"));
Debug.Log("Radius size is " + (int)myCircle.radius);
Debug.Log("donthit value " + LayerMask.GetMask("Enemies"));
Debug.Log("direction " + (myStats.player.transform.position - transform.position));
Debug.DrawRay(transform.position, other.transform.position - transform.position, Color.white);
Debug.DrawLine(transform.position, myStats.player.transform.position, Color.white);
//Destroy(hit.transform.gameObject);
Debug.Log (hit.transform.gameObject);
if(hit != null && hit.transform.gameObject != null){
if (hit.transform.gameObject.tag == "INDESTRUCTIBLE") {
Debug.Log("WALL");
// Destroy the Tag "Enemy" here
}
if (hit.transform.gameObject.tag == "Player") {
Debug.Log("player");
// Destroy the Tag "Enemy" here
}
Debug.Log("Tag name is " + hit.collider.tag);
}
Debug.DrawRay(transform.position, myStats.player.transform.position - transform.position, Color.white);
}
}
So it seems there are two things at work here. First, according to the documentation, Raycast 2D will also detect collider(s) at the start of the ray. If you don't use raycastAll then the source enemy will stop your ray before it goes out into the world. To prevent this you could use a layer mask. Just to be sure, a layer mask signifies the layer that you want to hit, not the layers you want to ignore. So to make sure the enemy layer is the only layer you ignore you can use this:
var layerMask = Physics2D.DefaultRaycastLayers & ~LayerMask.GetMask("Enemies");
And then use this mask in your raycast.
Second, there must be a reason you're not hitting anything else. If you applied the layermask in the wrong way then you filter out any hits against the wall or player. It looks like you both have an enemy tag and an enemy layer so make sure to get this straight. But if you used raycastAll without mask and still didn't hit anything then make sure your objects meet all the requirements. They must have 2D colliders of course. If they have colliders, make sure they either aren't triggers or that "Raycasts hit Triggers" is enabled in Edit -> project settings -> physics2d.
A final thing to check is whether or not your ray goes far enough. I noticed your debug draw does not reflect your raycast perfectly. Use this to draw the actual ray:
Debug.DrawLine(transform.position, transform.position + direction * (myCircle.radius + 1) / direction.magnitude, Color.white);