everyone! I am new to Unity and try to create a 2D arcade game by allowing my character to jump up at a trajectory motion to reach something then fall down. I use gravity scale equal to 1 and move_speed 1000 and rest_speed 500 but as soon as I hit space, the character just fell down instead of going up.
Also, walk.IsWalk is an int from Walking class(as soon as I hit right arrow, set IsWalk to 2 for example). My expected result would be as soon as we hold both space and right arrow key, the character should move upwards follow a curved motion. I dont know what is going on here, can anyone kindly point me to a direction? Thanks!
My code as follows:
using UnityEngine;
using System.Collections;
public class Jumping : MonoBehaviour
{
Rigidbody2D rb2D;
Animator anim;
Walking walk = new Walking();
public float move_speed;
public float rest_speed;
void Start()
{
rb2D = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
//float xmove = Input.GetAxis("Horizontal");
//float ymove = Input.GetAxis("Vertical");
//var curr = new Vector2(xmove, ymove);
if (Input.GetKey("space"))
{
anim.SetTrigger("JumpButtonPres");
rb2D.gravityScale = 1;
if (walk.isWalk == 2)
{
rb2D.AddForce(rb2D.position * move_speed);
}
else if (walk.isWalk == 1)
{
//rb2D.velocity += (Vector2.up * (-rb2D.velocity.x));
rb2D.AddForce(-rb2D.position * move_speed);
}
else
{
rb2D.AddForce(rb2D.position * rest_speed);
}
}
}
}
partial code for rigidbody2D.AddForce
It seems your problem is that whenever you are adding force to your rigidbody, you are multiplying it by the position where it's located, so by this i'm guessing your character is placed at a negative y component coordenate, and this is why rb2D.position * rest_speed will result on your playing falling. Change it for vector2.up * rest_speed and you should be fine.
Hope I was able to help.
Noé
Related
I am new to Unity so forgive me if I just did a stupid mistake. I am currently watching a tutorial from Brackeys, but I wanted to challenge myself by figuring out the movement myself. I got the moving forward correct, but I couldn't do the sideways movement. Since I've been spending a long time on the sideways movement, I just decided to watch the tutorial, but even when I used their code, it still didn't work. Can someone tell me why this isn't working? (FYI, in the code below, I did set all the public variables to some type of value).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sides : MonoBehaviour
{
public KeyCode left;
public KeyCode right;
public float sidewaysForce;
Rigidbody rb;
Vector3 v3;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetKey(left))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey(right))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
}
}
try left and right between quotation marks:
if (Input.GetKey("left"))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey("right"))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
You can check how to get the different keys in:
InputManager is in: Edit -> ProjectSettings -> Input
Other option is to use KeyCodes, which I prefer instead of the string argument for the intellisense benefits:
Update()
{
if ( Input.GetKey(KeyCode.UpArrow) )
//move up
if ( Input.GetKey(KeyCode.DownArrow) )
//move down
if ( Input.GetKey(KeyCode.RightArrow) )
//move right
if ( Input.GetKey(KeyCode.LeftArrow) )
//move left
}
If you're not getting the logs you added in the console, then you may have forgotten either:
To set the values of variables left and right in the inspector.
To add the Sides component to your GameObject.
I will assume your KeyCodes and the float are configured in the Inspector.
Either way you shouldn't do it like that!
You are completely overwriting the forward/backward movement and also the up/down movement with a hard 0 => you won't have any gravity and might break the forward movement - depending on which script is executed last.
You didn't share the forwards movement code so I can only guess but I suspect that there you do the very same thing like e.g.
rb.velocity = new Vector3(0, 0, someValue);
and therefore erase any sidewards movement from this script ;)
I would
a) make sure to have all movement input related stuff in a single script. Everything else just makes it unnecessarily hard to maintain and debug
b) make sure that your inputs don't eliminate each other
So something like e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
[SerializeField] private KeyCode _forward = KeyCode.W;
[SerializeField] private KeyCode _backward = keyCode.S;
[SerializeField] private KeyCode _left = KeyCode.A;
[SerializeField] private KeyCode _right = KeyCode.D;
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
// by default input is 0,0,0
var input = Vector3.zero;
// user input for forward
if(Input.GetKey(_forward)) input.z = 1;
else if(Input.GetKey(_backward)) input.z = -1;
// user input for sidewards
if(Input.GetKey(_left)) input.x = -1;
else if(Input.GetKey(_right)) input.x = 1;
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity
velocity.y = _rigidbody.velocity.y;
// finally assign back to rigidbody
_rigidbody.velocity = velocity;
}
}
In general though way more flexible and without having to configure and check the KeyCodes manually you could rather use Input.GetAxis and do e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity for the gravity
velocity.y = _rigidbody.velocity.y;
// finally assign back to the rigidbody
_rigidbody.velocity = velocity;
}
}
You can configure the mappings in Edit → Project Settings → Input Manager but by default the
Horizontal already refers to both A/D as well as LeftArrow/RightArrow
Vertical already refers to both W/S as well as UpArrow/DownArrow
and additionally GetAxis slightly interpolates the input so it doesn't immediately jump from 0 to 1 and back but rather increases and decreases smoothly over some frames.
If the latter bothers you you can also use GetAxisRaw which does not do the smoothing.
I just realized that I did in fact made a stupid mistake. In my other script that makes the player move forward, it sets the velocity to (0, 0, 100) in the FixedUpdate() which meant that the x axis doesn't change. Sorry for wasting your time.
So I'm testing around with Unity and Visual Studio, and as a starter project I'm working to replicate Zelda (NES) and I'm starting with movement.
So far, with much jank, I've figured out how to prevent diagonal movement, but I'm having trouble figuring out the next part. In the game, if favors vertical movement when there are 2 inputs, but if you hit a wall, it lets you move horizontally until nothing is blocking your vertical movement.
I do not know enough to even think about how to do this. Anyone have an idea that can set me the right direction? Or perhaps just a better way of doing it? I'm still learning C#
public float MoveSpeed;
public Rigidbody2D rb;
Vector2 movement;
void Update() {
MovementInput();
}
private void FixedUpdate() {
rb.velocity = movement * MoveSpeed;
}
void MovementInput() {
float mx = Input.GetAxisRaw("Horizontal");
float my = Input.GetAxisRaw("Vertical");
//This makes diagonal impossible, favoring vertical.
if (my != 0) {
mx = 0;
}
movement = new Vector2(mx, my).normalized;
}
Try to compare the horizontal and the vertical axis absolute value. The highest would gives you the good direction.
if (Math.Abs(my) > Math.Abs(mx)) {
// up or down
} else {
// left or right
}
Currently, I want to make a dash for the player in a 2D game in unity. I'm using game objects to the left and right of the player to use raycasts to check the distance from a wall in a certain area. If its too close to a wall, I need it to dash only that distance (to the wall) instead of the full distance (behind/through the wall).
This is my code for dashing in my player movement (DashCheckLeft is -0.5 from the player) (DashCheckRight is 0.5 from the player):
public float dashDistance = 3f;
float distance;
public Transform DashCheckLeft;
public Transform DashCheckRight;
private void Update() {
if(Input.GetKeyDown(KeyCode.LeftShift)) {
Debug.Log("Dashed");
Dash();
private void FixedUpdate() {
RaycastHit2D hitLeft = Physics2D.Raycast(DashCheckLeft.position, Vector2.left, -dashDistance);
RaycastHit2D hitRight = Physics2D.Raycast(DashCheckRight.position, Vector2.right, dashDistance);
if(hitLeft.collider != null) {
distance = Mathf.Abs(hitLeft.point.x - transform.position.x);
}
else if(hitRight.collider != null) {
distance = Mathf.Abs(hitRight.point.x - transform.position.x);
}
else {
distance = dashDistance;
}
}
private void Dash() {
rb.position = new Vector2(rb.position.x + distance, rb.position.y);
}
The problem now is that apperently I'm only dashing the distance between the player and the DashChecks (0.5f) instead of the intended 3f and I believe it may be because the raycasts are hitting the collider of the player somehow, but changing my player to the "Ignore Raycast" layer makes it fall into the floor, and also doesn't fix the issue.
I worked on this problem, and found the answer. Basically, your raycasts were detecting the player's collider too. There is a simple fix for this. You should just add the fourth argument to your raycast. The fourth argument is a LayerMask. A LayerMask is like a tag, but you can have several on at the same time. This is what I changed the code to (I made some extra adjustments too):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
Rigidbody2D rb;
public float dashDistance = 3f;
public LayerMask floor;
float distance;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
RaycastHit2D left = Physics2D.Raycast(transform.position, -transform.right, dashDistance, floor);
RaycastHit2D right = Physics2D.Raycast(transform.position, transform.right, dashDistance, floor);
if (left.collider != null)
{
print("left");
distance = Mathf.Abs(left.point.x - transform.position.x);
distance = -distance;
}
else if (right.collider != null)
{
print("right");
distance = Mathf.Abs(right.point.x - transform.position.x);
}
else
{
distance = dashDistance;
}
print(distance);
Debug.Log("Dashed");
Dash();
}
void Dash()
{
rb.position = new Vector2(rb.position.x + distance, rb.position.y);
}
}
}
(when I say 'world objects', I mean the objects that make up the terrain. For example the ground or trees) You should add a new layer by clicking on the world objects. Then, you should look at the top of the inspector, then click the 'Layer Default' dropdown menu, and click the 'Add Layer...' button. Then write a new name on that list. Go back to the world game object, and click the same dropdown menu, and select the layer that you want for the world. You should make sure that all of the world objects have the same layer. Go back to the player object, and in the inspector, set the 'floor' variable to the same layer as the world. If that didn't make sense, here is a link of how to set layers.
I'm trying to make an object on the ground follow a flying object, for example a drone leading a human (for now i'm just using shapes - a cube and a capsule). My cube follows the capsule like i desire but i want the cube to follow the capsule on the ground only, rather than go up on the y-axis with the capsule. Right now, it follows the capsule everywhere, I want the capsule to lead while the cube follows along on the ground.
I have done some research on Google and Youtube but I have not seen any results. Please let me know how I can achieve this.
This is the code script attached to the cube(ground object)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class follow_target : MonoBehaviour
{
public Transform mTarget;
float mSpeed = 10.0f;
const float EPSILON = 0.1f;
Vector3 mLookDirection;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
mLookDirection = (mTarget.position - transform.position).normalized;
if((transform.position - mTarget.position).magnitude > EPSILON)
transform.Translate(mLookDirection * Time.deltaTime * mSpeed);
}
}
If the ground is planar, you can just set the y component to 0 (or whatever the ground y vector is).
If the ground changes in topology, you can do a raycast down from the capsule to get the hit point (vector3). You can use the hit point y component for the height. After that you will need to set the cubes rotation so that it is aligned to the ground. You could do that with a raycast as well, there are a number of examples of that online.
I hope that helps get you in the right direction.
Assuming a flat ground on Y = 0
either make sure your objects sticks to the ground so set
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
if(difference.sqrmagnitude > EPSILONSQR)
{
// In general be aware that Translate by default moves in the
// objects own local space coordinates so you probably would rather
// want to use Space.World
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
var pos = transform.position;
// reset the Y back to the ground
pos.y = 0;
transform.position = pos;
}
}
or simply already map the direction down on the XZ plane (ignoring any difference in Y) like
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
// simply ignore the difference in Y
// up to you if you want to normalize the vector before or after doing that
mLookDirection.y = 0;
if(difference.sqrmagnitude > EPSILONSQR)
{
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
}
}
Since the android should fly or move in the air too I guess I should also use physics too ? Not sure.
I added some spheres as waypoints and I want the android to move between them randomly including random range of movement and rotation speed. For example 1-10 range randomly speed for both movement and rotation.
Now what it does when I'm running the game the NAVI (1) is just moving in rounds none stop on small area not sure why, But it's not moving smooth between the spheres.
The idea is to make the NAVI droid to act like it's out of order.
This is screenshot of the NAVI (1) and it's inspector:
This screenshot is of the Waypoints attached to it Waypoints script:
This is the Waypoints script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public Transform objectToMove;
public float moveSpeed = 3.0f;
public float rotationSpeed = 3.0f;
public GameObject[] waypoints;
public int currentWP;
private void Awake()
{
}
private void Start()
{
currentWP = 0;
}
private void Update()
{
if (waypoints.Length == 0) return;
var lookPos = waypoints[currentWP].transform.position - objectToMove.position;
lookPos.y = 0;
var rotation = Quaternion.LookRotation(lookPos);
objectToMove.rotation = Quaternion.Slerp(objectToMove.rotation, rotation, Time.deltaTime * rotationSpeed);
objectToMove.position += objectToMove.forward * moveSpeed * Time.deltaTime;
}
}
You are never changing currentWP. That means at every frame, you're rotating toward the same nav point and moving the droid back towards it.
You have to change the currentWP only when the droid reaches it. Use OnTrigger between the target nav point and the droid.
You can do it something like this .
//for example you've got 5 waypoints
currentWaypoint = Random.Range(1, 5);
Hope it helps