My bat stays still in the air.
How can i check that the player, that is moving right below on x axis, comes from the left or right side relative to the bat?
public class Bat : MonoBehaviour
{
Player player;
void Start()
{
player = FindObjectOfType<Player>();
}
void Update()
{
if (transform.InverseTransformPoint(player.transform.position).x >= 0)
transform.rotation = new Quaternion(transform.rotation.x, 0f, transform.rotation.z, 0f);
else
transform.rotation = new Quaternion(transform.rotation.x, -180f, transform.rotation.z, 0f);
}
}
As you may notice i try to flip the the Bat so it actually looks at the player.
First, don't use new Quaternion(...); unless you absolutely, 100% know quaternions inside and out. For instance, having the w component be 0 results in zero effective rotation. Also, quaternions use unitless figures and not degrees for their components. See here for a nice visualization of what different quaternion values might look like, under Quaternion).
Second, your logic is weird because if the bat has zero (identity) rotation, and the player is on the left of the bat, then your logic tries to* flip the bat 180 degrees, so that the player is now on the right side of the bat.
Then, the next frame assuming the bat and player are still in the same positions, the logic says oh the player is on the right side, set the rotation to zero (identity) rotation, which is of course what it was in the first place. So basically, you would have the bat rotate 180 degrees again so that the player is once again on the left side of the bat.
And so, you could get into a situation where every frame, the bat would flip a complete 180. Definitely not desired.
* I'm assuming if you had used Quaternion.Euler(transform.eulerAngles.x, 0f, transform.eulerAngles.z) etc.
Instead of concerning with any of that, use Vector3.Cross to find the forward the bat should point so its right faces the player's position. Then, use Quaternion.LookRotation to set the rotation of the bat to point in that forward direction:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
If you have the player on a different z as the bat, this will cause the bat to rotate around the y axis accordingly, which is what you would want from a 3d game, and could be a neat effect for a 2d game if you want that kind of effect.
If you want it to ignore the z position of the player, you could zero out the z component of batRightDir...
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
batRightDir.z = 0; // ignore Z differences between bat and player
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
or it may be more intuitive to branch as you were previously:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
if (batRightDir.x > 0)
{
transform.rotation = Quaternion.identity;
}
else if (batRightDir.x < 0)
{
transform.rotation = Quaternion.LookRotation(Vector3.back);
}
else
{
// player above or below bat. do nothing?
}
}
You can get the access to his Rigidbody and than call it.
If Rigidbody.velocity.x > 0 he is moving to the right. If it's <0 it's moving to the left. All is related to X axis of course.
Related
I have a character/rigidbody and 'she' can turn around. When I press Play in Unity, if I move forward/backward, that's fine, she moves forward/backward. It's a good start.
But then if I turn her left or right, then go forward/backward, she now moves sideways.
She is a rigidbody component set as a parent in the scene.
Surely it's not difficult to do, but I can't figure out how to set her rotation so that when she turns, she will move 'forward' when I press the button to move her forward! There are plenty of first-person-shooter games where you can turn and move 'forward' and the player goes in the correct direction.
My rotation script at the moment is this:
Vector3 EulerAngleVelocity;
public float rotateSpeed = 250.0f;
void Update() {
if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.DownArrow))
{
MoveVector = PoolInput();
Move();
}
if (Input.GetKey(KeyCode.RightArrow))
{
EulerAngleVelocity = new Vector3(0, rotateSpeed, 0);
Quaternion deltaRotation = Quaternion.Euler(EulerAngleVelocity * Time.deltaTime);
rigidbody.MoveRotation(rigidbody.rotation * deltaRotation);
}
}
private void Move()
{
rigidbody.AddForce((MoveVector * moveSpeed));
}
private Vector3 PoolInput()
{
Vector3 dir = Vector3.zero;
dir.x = joystick.Horizontal();
dir.z = joystick.Vertical();
if (dir.magnitude > 1)
dir.Normalize();
return dir;
}
You're moving your joystick and adding that direction in relation to the WORLD instead of in relation to your player. If you want to add force relative to the orientation of the RigidBody probably what you want to use is rigidBody.AddRelativeForce (documentation) instead of simply rigidBody.AddForce.
Your problem isn't your rotation code, it's your movement code. You're applying a motion in world-space, not local-space ('object'-space).
For example, if you're using Vector3.Forward, you will want to use transform.Forward instead.
When moving the player I use this code for the player rotation. The player should always rotate to the target point he will move to.
private void SetPlayerRotation(Vector3 targetCellPosition)
{
Vector3 targetPoint = new Vector3(targetCellPosition.x, transform.position.y, targetCellPosition.z) - transform.position;
transform.rotation = Quaternion.LookRotation(targetPoint, Vector3.up); // rotate the player
}
Sometimes the console logs
Look rotation viewing vector is zero
How can I fix this?
That is being logged if vector is zero, making an if statement will fix this. The reason behind logging of this is when rotation vector is 0 nothing happens so doing this task is pointless.
if (targetPoint != Vector3.Zero) {
transform.rotation = Quaternion.LookRotation(targetPoint, Vector3.up);
}
Needed help here, I've been following this tutorial about Enemy Patrolling, https://www.youtube.com/watch?v=KKU3ejp0Alg
The tutorial tell us to set 2 Empty Game Objects and the coordinates so the sprite will walk from one direction to another, the walking does working, however the rotation of the sprite became really weird, the 'Worm' sprite is suppose to move left and right now it change into facing upward
Please look at this image,
When moving from left to right
When moving from right to left
public Transform[] patrolPoints;
public float speed;
Transform currentPatrolPoint;
int currentPatrolIndex;
// Use this for initialization
void Start()
{
currentPatrolIndex = 0;
currentPatrolPoint = patrolPoints[currentPatrolIndex];
}
// Update is called once per frame
void Update()
{
transform.Translate(Vector3.up * Time.deltaTime * speed);
if (Vector3.Distance(transform.position, currentPatrolPoint.position) < .1f) {
if (currentPatrolIndex + 1 < patrolPoints.Length)
currentPatrolIndex++;
else
currentPatrolIndex = 0;
currentPatrolPoint = patrolPoints[currentPatrolIndex];
}
Vector3 patrolPointDir = currentPatrolPoint.position - transform.position;
float angle = Mathf.Atan2(patrolPointDir.y, patrolPointDir.x) * Mathf.Rad2Deg - 90f;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.RotateTowards(transform.rotation, q, 180f);
}
This is the code
Thank you
You can pass the optional parameter Space.World to your translation to move your object relative to world space instead of object space. That should prevent your object from rotating.
transform.Translate(Vector3.up * Time.deltaTime * speed, Space.World);
You can read more about it in the documentation.
You probably rotated the enemy sprite by 90 degrees. Try placing all your scripts into empty game object with no rotation, than place the enemy sprite as child of the empty game object and rotate as you need. This way your logic won't collide with your graphics.
What I want to do is to make a kind of 2.5D runner game in Unity, which the character's all three rotation axises are frozen and the position on Z axis is also frozen. I don't know how to make the character moving forward nicely on the seesaw. (I create the seesaw by using HingeJoint.)
I create a struct to detect the CapsuleCollider status by using Physics.Raycast() function and that works fine.
private struct ColliderStatus
{
public bool headed; //colliding up
public bool footed; //colliding down
public bool onPlane; //colliding down && the obstacle colliding does not have slope angle
public bool lefted; //colliding left
public bool righted; //colliding right
public bool inAir; //not colliding anything
}
I've tried these ways:
Add force on Rigidbody to move forward
//To move character rigidbody move forward automatically in runner game
//when the speed is lower than the minimum speed and it's on plane or in air.
if (rigidbody.velocity.x < minForwardSpeed && (colliderStatus.onPlane || colliderStatus.inAir))
{
rigidbody.AddForce(20f * Vector3.right);
}
//Add gravity to player
Vector3 gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
rigidbody.AddForce(gravityForce);
It doesn't work well because the character continue going up when it's on the seesaw though the seesaw starts to tilt. And there will be a velocity loss when the character fall to ground from a higher plane or after jumping and what it looks like is that the character will stunned for a little moment on the landing point and then begin to accelerate.
Use transform.Translate() to move forward && change the way of adding gravity
//Use transform.Translate() to move forward
//I recognize that by this way, there will be no velocity loss
//when the character falling down to the ground at the landing point
//If I don't use this condition, my character will stuck on the
//right vertical wall
if (!colliderStatus.righted)
{
transform.Translate(new Vector2(minForwardSpeed, 0f) * Time.deltaTime);
}
I don't know why I can't write like this since it will cause the velocity doesn't react correctly:
//Use transform.Translate() to move forward
if (!colliderStatus.righted && rigidbody.velocity.x < minForwardSpeed)
{
transform.Translate(new Vector2(minForwardSpeed, 0f) * Time.deltaTime);
}
To change the way of adding gravity, I use a function SlopeAngleVector() to calculate the slope vector the character is running on.
private Vector3 SlopeAngleVector()
{
Vector3 nextStepPositon = new Vector3(transform.position.x + 0.01f, transform.position.y, 0f);
Ray nextPosRay = new Ray(nextStepPositon, Vector3.down);
Ray nowPosRay = new Ray(transform.position, Vector3.down);
RaycastHit nextPosHit;
RaycastHit nowPosHit;
Vector3 slopeAngle = Vector3.zero;
Physics.Raycast(nowPosRay, out nowPosHit, 5f, obstaclesLayerMask);
if (Physics.Raycast(nextPosRay, out nextPosHit, 5f, obstaclesLayerMask))
{
slopeAngle = new Vector3(nextPosHit.point.x - nowPosHit.point.x, nextPosHit.point.y - nowPosHit.point.y, 0f).normalized;
}
return slopeAngle;
}
Then I add the gravity by calculate the gravity projection on the slope vector:
private void AddGravity()
{
Vector3 gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
//my character could be collided by the long vertical wall(colliderStatus.righted)
//so I set the condition as "!colliderStatus.footed"
//otherwise, I would use "colliderStatus.inAir"
if (!colliderStatus.footed)
{
gravityForce = new Vector3(0f, -gravityOnPlayer, 0f);
}
else
{
gravityForce = Vector3.Project(Vector3.down * gravityOnPlayer, SlopeAngleVector());
}
rigidbody.AddForce(gravityForce);
}
Now my character can slide down from the seesaw but it will keep going backwards. And it cannot make it through when on the low slope angle seesaw.
How to make a good behavior script for the runner on seesaw?
I'd suggest looking at some of the Unity standard asset character controllers, I believe they take slopes into account for their character movement. It may give you some ideas.
I'd also recommend modifying the way your code calculates the angle of the slope. The raycast hit will give you back a surface normal, you should then be able to use the Vector3.Cross to figure out the angle of the slope.
It'll be something like: Vector3.Cross(normal, (vector that points away from screen)).
You may need to tweak it to get it working correctly but this can give you the slope angle in one raycast. It may also eliminate potential issues of your move to position being just below the see saw.
As a general tip, try not to mix transform and rigidbody stuff together, if you want to move the rigidbody, move the rigidbody directly, not indirectly through the transform.
I have a ball which rotates around the point 0,0,0 in the Z-axis. When the space button is pressed, the ball has to go inside the large circle. Now my code looks like this. When you press space, the ball does not behave as they should. I want to know how to make a balloon down exactly down
that's how the ball should behave ->
behavior image
my code:
void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
transform.position = new Vector3 (transform.position.x - 1, transform.position.y - 1, 0);
} else {
transform.RotateAround(new Vector3(0,0,0), new Vector3(0,0,1), 2);
}
}
Your code to 'jump' the orbit doesn't do what you want because Transform.RotateAround modifies both the rotation and the position of the object's transform.
So jumping to (position - 1,1,0) in the world is going to return wildly different results every time.
What you want to do instead is calculate the (Vector) direction from the object to the centre of orbit (the difference), then scale that down to how far you want it to move, then apply it to the position.
private Vector3 _orbitPos = Vector3.zero;
private float _orbitAngle = 2f;
private float _distanceToJump = 2f;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var difference = (_orbitPos - transform.position).normalized * _distanceToJump;
transform.Translate(difference);
}
transform.RotateAround(_orbitPos, Vector3.forward, _orbitAngle);
}
This will move the object to be orbiting 2 units closer when space is pressed immediately.
If you wanted to have a smooth transition instead of a jump, look into using Mathf.Lerp, Vector3.Lerp and the routines involved.