i'm currently working on a small game in Unity, and i use c# for my code.
The game is a small role playing game, where you control a character like you do in games like world of warcraft or dragon age.
You run around on small maps, with hills around the map. Currently, you can walk up the hills at some places and i want to stop that.
so i thought i write a code where i get the last position of my character and the current position, than i get the angle between them and if the angle is to high, i would force the player to go back (or disable moving forward).
this is what i got for the code so far:
Vector3 lastPos; // last Position
Vector3 curPos; // current Position
float slopeAngle; // The angle between curPos and lastPos
void Start()
{
curPos = transform.position;
}
void FixedUpdate()
{
lastPos = curPos;
curPos = transform.position;
slopeAngle = Vector3.Angle(lastPos, curPos);
}
unfortunately, slopeAngle is mostly between 0 and 0.04. it doesn't matter if i run up a steep mountain or just a flat hill.
any ideas?! (if there is a better solution, than getting the angle between two points, i am very interested!! ) :)
Thanks for your help!
You could try using a Raycast from your character to check if there's a slope high enough to halt your movement.
float maxSlopeAngle;
Transform raycastOrigin;
void FixedUpdate()
{
RaycastHit hit;
Vector3 direction = Quaternion.Euler(-MaxSlopeAngle, 0, 0) * transform.forward;
if (Physics.Raycast(raycastOrigin.position, direction, out hit, 1f))
{
Debug.Log("Slope ahead!");
}
}
Obviously, you'd want to figure out the right maxDistance for the raycast so you won't stop too soon.
Also, you can use the following script to help you figure out the right angle:
[ExecuteInEditMode]
public class SlopeAngleHelper : MonoBehaviour {
public float MaxSlopeAngle;
public Transform RayOrigin;
void Update ()
{
Vector3 direction = Quaternion.Euler(-MaxSlopeAngle, 0, 0) * transform.forward;
Debug.DrawRay(RayOrigin.position, transform.forward, Color.black, 0.01f, false);
Debug.DrawRay(RayOrigin.position, direction, Color.red, 0.01f, false);
}
}
Just put it on your character object and it will "visualize" the raycast in scene view.
Related
I've been doing some research on why my player(GameObject) is does not rotate toward my mouse position in my TopDown 3D game and I can't seem to find what is wrong with my code, so im making this post. The problem thats I have is that only the GameObject of my player (in my case, a capsule) rotate toward my mouse position but the axis of my player stays the same. In other word, I can't rotate the axis of my player, to face my mouse position, but I can rotate the GameObject of my player to face my mouse position. Its really hard to explain and this never happened to me before. Question is how can I rotate the axis of my player to face my mouse position. Keep in mind that my game is a top down view.
Here is the code im using for my playerMouvment and for my mouseLook:
public class Controller : MonoBehaviour
{
public float moveSpeed = 6;
Rigidbody rb;
Camera viewCamera;
Vector3 velocity;
void Start()
{
rb = GetComponent<Rigidbody>();
viewCamera = Camera.main;
}
void Update()
{
Vector3 mousePos = viewCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, viewCamera.transform.position.y));
transform.LookAt(mousePos + Vector3.up * transform.position.y);
velocity = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized * moveSpeed;
}
void FixedUpdate()
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
}
Again I tried and look for any error in my code and I can't find anything that cause this weird situation and so if anyone can help me find a better way to write this code and solve my problem it would be great!
If I understand you correctly everything is fine with your game and the player is turning as it should, but only in the editor that the axis of the player (red, green and blue arrows) don't turn with the player?
If this is the problem it might be that you are using global space handle instead of local space. Clicking the icon I highlighted in the image should do the trick.
Use this code instead of LookAt() function
Vector2 direction = mousePos.position - player.transform.position;
player.transform.right /* Maybe you need Up or -Up or -right */ = direction;
This would work too in some cases
Vector2 direction = new Vector2
(
mousePos.position.x - player.transform.position.x,
mousePos.positoin.y - player.transform.position.y
)
player.transform.right /* Maybe you need Up or -Up or -right */ = direction;
Ok, I have a 3D Unity game built for iOS where these controls will be implemented (in a first person shooter manner):
Camera follows just behind character
Keeping finger on screen makes character walk, finger up makes it stop
THIS is the problem: wherever the user has started touching the screen, whatever direction they drag (forward, backwards, left right) the character will walk indefinitely in that direction.
This must have done already but I cant find anything anywhere. I have the camera as a child of the character, so that's taken care of but for char movement all I have is:
void Update()
{
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
Vector3 target = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -10f));
transform.Translate(Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime) - transform.position);
}
Which doesn't work as needed. What can I do here?
You can just try to use any king of UI joysticks (https://assetstore.unity.com/packages/tools/input-management/joystick-pack-107631) for example
And access its parameters something like this:
public class TestMovement : MonoBehaviour
{
public float Speed;
public FloatingJoystick Joystick; //Set in the inspector, prefab from the asset
private void Update()
{
transform.Translate(new Vector2(Joystick.Horizontal, Joystick.Vertical) * Speed * Time.deltaTime);
}
}
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.
I have a character in a 2D game, my goal is to get his eyes look as if they are looking at the cursor by moving the circles used for pupils towards the cursor, with limits. I have been stuck trying to create this for two days now and have yet to find a way that works!!
Using the following code I was able move my eyes towards the cursor, however this only works around the bottom left of the screen! (If the cursor is not below the screen or to the left of the screen the eyes move to the top right)
using UnityEngine;
using System.Collections;
public class LookAtMouse : MonoBehaviour {
public float speed = 5f;
private Vector3 target;
public Transform origin;
void Start () {
target = transform.position;
}
void Update () {
target = (Input.mousePosition);
target.z = transform.position.z;
transform.position = Vector3.MoveTowards(origin.position, target, speed * Time.deltaTime);
}
}
If anyone could point me in the right direction I would be incredibly grateful!
Thank you :)
Likely the reason it's only working in one quadrant for you is because Input.mousePosition returns a position in pixel coordinates. Basically, if your window is 800x600 pixels, it will return...
(0, 0, 0) for the bottom left pixel of the screen
(0, 600, 0) for the top left pixel of the screen
(800, 0, 0) for the bottom right pixel of the screen
(800, 600, 0) for the top right pixel of the screen
Since your eye pupil is in world space, you want the mouse position in world space also. Either way, even if you fixed that, I don't think Vector3.MoveTowards is going to do quite what you're wanting to do. I think this is more along the lines of what you want:
using UnityEngine;
public class LookAtMouse : MonoBehaviour {
public float speed = 5f;
public float maxDistance = 1f;
public Camera mainCamera;
private Vector3 _origin;
void Start () {
_origin = transform.position;
}
void Update () {
/* Get the mouse position in world space rather than screen space. */
var mouseWorldCoord = mainCamera.ScreenPointToRay(Input.mousePosition).origin;
/* Get a vector pointing from initialPosition to the target. Vector shouldn't be longer than maxDistance. */
var originToMouse = mouseWorldCoord - _origin;
originToMouse = Vector3.ClampMagnitude(originToMouse, maxDistance);
/* Linearly interpolate from current position to mouse's position. */
transform.position = Vector3.Lerp (transform.position, _origin + originToMouse, speed * Time.deltaTime);
}
}