I am currently programming a game where the player must tilt their phone to rotate the world so they can navigate through a maze. This is proving difficult as when I rotate the phone the sprite flies off screen.
I am assuming this is to do with the forces between the character's and the world's colliders which ends up launching the character.
However, I want the character to have gravity follow the players relative down direction so that when the character rotates it falls the direction of its feet.
Therefore, how should I rotate the direction of gravity to be relative to the sprite's direction and prevent the player from flying off when rotating?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GyroController : MonoBehaviour {
private Gyroscope Gyro ;
private float rotation;
private Vector3 startEulerAngles;
private Vector3 startGyroAttitudeToEuler;
public GameObject Player = null;
private void Awake () {
if (SystemInfo.supportsGyroscope) {
Gyro = Input.gyro;
Gyro.enabled = false;
startEulerAngles = transform.eulerAngles;
startGyroAttitudeToEuler = Input.gyro.attitude.eulerAngles;
Debug.Log("Gyro Enabled");
}
else {
Debug.Log("No Gyro Detected");
}
}
private void Update()
{
Vector3 deltaEulerAngles = Input.gyro.attitude.eulerAngles - startGyroAttitudeToEuler;
deltaEulerAngles.x = 0.0f;
deltaEulerAngles.y = 0.0f;
Player.transform.eulerAngles = startEulerAngles - deltaEulerAngles;
}
}
So far I have only tried one method of changing the direction of gravity which involves moving the center of gravity which only made the character fall through other colliders. Hence, my gravity code is not included in this snippet.
I am not asking for the code to be made for me, just a direction for me to look in as i've hit a dead end using rigidbody2d
Thankyou.
Related
I have been working on a 2d top down rpg game and I have added walking animations etc, I want to stop the player from doing a walking animation when they hit a wall and currently I have a box collider with a ray cast, the ray cast originally hit the player box collider when walking down but after using a layermask this has stopped, however while walking left and right work perfectly two issues occur that I cannot seem to fix. First, when walking up or down into a tilemap that is on the collision layer (this tilemap has tilemap collider which will stop the player from walking through them) the animation still plays, and second the player will only collide once instead of repeatedly when hitting the tilemap when two tiles are placed back to back, here is my code for collision, the tiles that are for collision are on layer 6.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerScript : MonoBehaviour
{
public float moveSpeed;
private Animator ani;
private bool isMoving;
private Vector2 lastMove;
private Rigidbody2D body;
private Vector2 movement;
private LayerMask wallLayer = 1 << 6;
// Start is called before the first frame update
void Start()
{
body = GetComponent<Rigidbody2D>();
ani = GetComponent<Animator>();
movement = Vector2.zero;
isMoving = false;
}
// Update is called once per frame
void Update() {
isMoving = false;
movement = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
RaycastHit2D ray = Physics2D.Raycast(body.position, movement, 0.6f, wallLayer);
if((movement.x != 0f || movement.y != 0f) && !(ray && ray.collider.tag == "wall")) {
isMoving = true;
lastMove = movement;
}
ani.SetFloat("MoveX", movement.x);
ani.SetFloat("MoveY", movement.y);
ani.SetFloat("LastX", lastMove.x);
ani.SetFloat("LastY", lastMove.y);
ani.SetBool("IsMoving", isMoving);
}
void FixedUpdate() {
body.MovePosition(body.position + movement * moveSpeed * Time.deltaTime);
}
}
The code looks fine. Most likely, the issue is with your configuration of the AnimatorController.
Do you have transitions in place for (for example) coming from the walk animation back to idle?
I'm currently trying to make the camera follow the player smoothly. The script works fine but the problem is that this script is causing the player to stutter at a certain point. For example, if the player is at X:3, the player would stutter but then if the player was at X:-6, the player would stop stuttering. I'm 100% sure that this script is the problem because if I remove the script, the player stops stuttering.
Here is the camera follow script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollowing : MonoBehaviour
{
[SerializeField]
private Transform target;
[SerializeField]
private Vector3 cameraOffset;
[SerializeField]
private float followSpeed = 10f;
[SerializeField]
private float xMin = 0f;
private Vector3 velocity = Vector3.zero;
private void FixedUpdate()
{
Vector3 targetPos = target.position + cameraOffset;
Vector3 clampedPos = new Vector3(Mathf.Clamp(targetPos.x, xMin, float.MaxValue), targetPos.y, targetPos.z);
Vector3 smoothPos = Vector3.SmoothDamp(transform.position, clampedPos, ref velocity, followSpeed * Time.fixedDeltaTime);
transform.position = smoothPos;
}
}
If you know the answer or possible causes please tell me, I'm trying to release, publish, etc, this game by the end of this year. Thanks! :D
By stutter I think you mean shake or tremble (not familiar with the stutter word).
I would try to adjust to the docs example. Use void Update() or LateUpdate() instead of FixedUpdate().
You may want to use void LateUpdate() if you want your game to be accurate. This method will be called after the input is detected, so it will react better. I believe that this is the better choice, because it will be more accurate than Update().
I would also keep track of where in the scene the shake starts if its from a determined point on, and check for some misplaced collider near the position where the undesired shake starts.
NavmeshAgent player not parallel to slope of hill when moving over hill. On plane surface its going smoothly.
See Video
Below Image properties of navMesh and player
https://ibb.co/fijmoV
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class SampleAgentScript : MonoBehaviour {
public Transform target ;
NavMeshAgent agent;
// private static bool start1=false , start2=false, start3;
// Use this for initialization
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
//if white button click moves to targer-1
agent.SetDestination(target.position);
}
}
I am not sure if NavmeshAgent is supposed to do that for you. This looks like something you're supposed to do manually.
You can correct the rotation of the character to match the slope by performing a raycast downwards and obtaining the normal of the hit point. After obtaining the normal of the hit point, you can then calculate the new rotation with that normal hit point. There are many ways to do that calculation but using Quaternion.FromToRotation and lerping the rotation with Quaternion.Lerp seems to work best.
Finally, make sure to the raycast is only done to Objects you considered as a "Hill" or "Ground". You can do this with the bitwise operation on the layer the "Hill" or "Ground" objects are placed on. The example below assumes that the Objects you consider as "Hill" or "Ground" are on a layer called "Hill".
//Reference of the moving GameObject that will be corrected
public GameObject movingObject;
//Offset postion from where the raycast is cast from
public Vector3 originOffset;
public float maxRayDist = 100f;
//The speed to apply the corrected slope angle
public float slopeRotChangeSpeed = 10f;
void Update()
{
//Get the object's position
Transform objTrans = movingObject.transform;
Vector3 origin = objTrans.position;
//Only register raycast consided as Hill(Can be any layer name)
int hillLayerIndex = LayerMask.NameToLayer("Hill");
//Calculate layermask to Raycast to.
int layerMask = (1 << hillLayerIndex);
RaycastHit slopeHit;
//Perform raycast from the object's position downwards
if (Physics.Raycast(origin + originOffset, Vector3.down, out slopeHit, maxRayDist, layerMask))
{
//Drawline to show the hit point
Debug.DrawLine(origin + originOffset, slopeHit.point, Color.red);
//Get slope angle from the raycast hit normal then calcuate new pos of the object
Quaternion newRot = Quaternion.FromToRotation(objTrans.up, slopeHit.normal)
* objTrans.rotation;
//Apply the rotation
objTrans.rotation = Quaternion.Lerp(objTrans.rotation, newRot,
Time.deltaTime * slopeRotChangeSpeed);
}
}
I am building a 3D maze that is moved by the player. I'm taking a "labyrinth" approach and having a small ball be maneuvered through the maze when the player moves the maze. The problem occurs when the player moves a wall that the ball is currently resting on in the direction of the ball. The ball passes through the wall and stops on the next available wall.
The maze uses a mesh collider and rigidbody and the ball is a sphere collider.
I have drastically increased the physics frame rate to no avail. Considering the complexity of the maze and the potentially large number of maze combinations I really don't want to attach simple colliders to every wall. Any tips, tricks, suggestions, comments, etc. would be appreciated.
Ball Script for continious rotation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ballGravity : MonoBehaviour {
public Rigidbody m_Rigidbody;
public Vector3 m_EulerAngleVelocity;
// Use this for initialization
void Start () {
m_EulerAngleVelocity = new Vector3 (0, 100, 0);
m_Rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate () {
Quaternion deltaRotation = Quaternion.Euler (m_EulerAngleVelocity * Time.deltaTime);
m_Rigidbody.MoveRotation (m_Rigidbody.rotation * deltaRotation);
}
}
Maze Rotation Script:
using UnityEngine;
using System.Collections;
public class rotObj : MonoBehaviour
{
private float baseAngle = 0.0f;
float rotSpeed = 10;
void OnMouseDown(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
baseAngle = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
baseAngle -= Mathf.Atan2(transform.right.y, transform.right.x) *Mathf.Rad2Deg;
}
void OnMouseDrag(){
//float rotY = Input.GetAxis("Vertical")*rotSpeed*Mathf.Deg2Rad;
//gm.transform.Rotate(Vector3.right, rotY);
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) *Mathf.Rad2Deg - baseAngle;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
}
To get consistent physics state its best to move colliders within FixedUpdate - if you move colliders in a OnMouseXX stage of the pipeline (Consider consulting https://docs.unity3d.com/Manual/ExecutionOrder.html), you risk that the change might be missed next time FixedUpdate is called. I.e. your ball moves through the state of your scene based on how it was before you rotated the maze, and with some unfortunate timings its possible that collisions get missed.
If you already tried with a tighter timestep, consider queueuing the OnMouseDrag change, like store the resulting rotation in a variable, and apply it in FixedUpdate, so it is applied just in case for next physics computation.
you may also consider moving your ball at the same time as you rotate your maze, quickly moving a mesh collider against a much smaller rigidbody is a sure source of trouble. Can you move a camera instead ?
Sorry if the title is confusing. Basically, what I'm trying to do is make sure the player is grounded (so they can jump again), problem is in my game there will be convex shapes that the player could land on.
The current way I'm doing this is with a raycast, but since the single raycast could only come from somewhere like the centre, I made it so the raycast was going along the bottom of the player instead (this removed the problem where the player couldn't jump if more than half of their body was off a platform).
The raycast along the bottom however made it so if I tried to jump while on a shape like a ramp, it would cause me to go incredibly high, which I don't want.
Can anyone help me fix this?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMovement : MonoBehaviour {
public Rigidbody2D rBody;
[Range(1, 10)]
public int Speed;
private float lastDistance;
public bool isGrounded = true;
private LayerMask environment;
void Start () {
environment = LayerMask.GetMask("Environment");
}
void FixedUpdate() {
// Check if the user is attempting to jump
if (Input.GetKey(KeyCode.A)) {
rBody.velocity = new Vector2(-Speed, rBody.velocity.y);
}
if (Input.GetKey(KeyCode.D)) {
rBody.velocity = new Vector2(Speed, rBody.velocity.y);
}
if (Input.GetKey(KeyCode.Space)) {
RaycastHit2D hit2D = Physics2D.Raycast(rBody.position+new Vector2(0,-1.5f), Vector2.right,1f,environment);
if (hit2D) {
if (hit2D.distance < lastDistance) {
lastDistance = hit2D.distance;
}
else {
lastDistance = 100f;
rBody.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
}
}
}
}
What happens if you try with Input.GetKeyDown(KeyCode.Space)?
Unless the game design it to keep jumping as long as you press space, this might be the reason you are jumping extra high.
If the Raycast solution is still wonky, another way of detecting if the player is grounded is using a Collider for the floor and a flag that tells you when you're colliding with it (you can set this flag using OnCollisionEnter/Exit or OnTriggerEnter/Exit)