I'd like to thank you in advance for helping me with this issue.
I have a player that consists of three part:
The body
The head
and the Arm
All three of these are individual sprites.
I have grouped them all under one Empty GameObject that I named "Player"
I have then added an animation on the "Player" GameObject that changes the Y position from 0 to 0.022, and then from 0.022 back to 0.
This create a nice little hover animation where the character bounces up and down.
The animation works perfectly fine!
However, I also have a script attached to the "Player" object, this script is called "PlayerController".
In PlayerController I change the player's x Position if they hold down A (for leftwards movement) or D (for rightwards movement).
protected bool facingRight = true;
float xPos = 0;
void Update()
{
if (Input.GetKey(KeyCode.D))
{
xPos += 0.01f;
if (!facingRight)
{
Flip();
}
}
else if (Input.GetKey(KeyCode.A))
{
xPos -= 0.01f;
if (facingRight)
{
Flip();
}
}
gameObject.transform.position = new Vector2(xPos, transform.position.y);
}
protected void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
When I hold A or D, the character refuses to move. However he does flip correctly.
I personally think my animation is fighting the script, and that the animation has a higher priority than the script...?
Here's a video of me explaining my issue
(Not sure if videos are generally accepted in this community or not. If not, then I apologize)
If you guys could please help me understand what is happening and how to correct it I would be extremely grateful.
Thank you! :)
Solved the problem after hours of tinkering.
Turns out the solution was simple:
All I had to do was structure my player like this:
Player (Empty GameObject)
PlayerAssembly (Body, Head, and Arm's parent)
Body Parts (Body, Head, Arm)
and then put the script, rigidBody2D, and collider on "Player".
Then put the animator on "PlayerAssembly".
Related
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
}
I am creating a simple mini-game in unity as a separate scene. The problem is that the gravity works completely fine on my rigidbody when I start from the mini-game scene, but when I move to the mini-game scene from another the gravity stops working and my player can only go up.
Here is what I've tried:
My Project's Physics 2D settings are fine (Gravity: X = 0, Y = -9.81)
My Project's Time setting is fine (Time Scale: 1)
My Player character has a Rigidbody2D, which is Dynamic, Simulated (true), Gravity Scale = 5, ONLY rotation is frozen, and it's set to never sleep.
The Player also has box and circle colliders (both 2D).
All player inputs are read in Update(), and all physics are done in FixedUpdate() (I am using velocity to move the player).
I do have a GameController object which is carried between the scenes, but none of it's scripts should affect the scene (I can provide the OnLevelWasLoaded() parts of the scripts or any other parts).
Here is the code in my player controller:
void Start()
{
rb = GetComponent<Rigidbody2D>();
box = GetComponent<BoxCollider2D>();
_renderer = GetComponent<SpriteRenderer>();
velVer = 30f;
velHor = 10f;
}
void Update()
{
moveHor = Input.GetAxisRaw("Horizontal");
moveVer = Input.GetAxis("Vertical");
buttonUp = Input.GetButtonUp("Vertical");
_moveVector = new Vector2(moveHor, moveVer);
}
void FixedUpdate()
{
if (IsGrounded() && buttonUp)
rb.velocity = new Vector2(velHor * _moveVector.x, velVer * _moveVector.y);
if (_moveVector.x != 0 && _moveVector.y == 0)
rb.velocity = new Vector2(velHor * _moveVector.x, rb.velocity.y);
}
private bool IsGrounded()
{
RaycastHit2D raycast = Physics2D.BoxCast(box.bounds.center, box.bounds.size, 0f, Vector2.down, 0.1f, layer);
return raycast.collider != null;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag.Equals("Heart"))
{
other.gameObject.SetActive(false);
}
}
I have commented out any references to the GameController object from all scripts in the mini-game scene, so the GameController should not have any impact on my mini-game character controller.
I am desperate, I have searched online for more than 3 hours and haven't found anything that could help me (if I did actually miss a solution my apologies).
I can provide any further information you may require.
EDIT:
Here are Gifs of character behaviour when I start the game from the mini-game scene, and when I move to the mini-game scene from another.
Working (started mini-game directly):
Not Working (moved to mini-game from another scene):
Check for three things:
1) Are you sure that you didn't set timeScale to 0 in any other script?
2) Does your player have the same scale on both levels?
3) Why do you use Physics2D.BoxCast in IsGrounded function? I usually use Raycast2D for the ground checking process.
Okay I have found the issue! I thought the problem would lie inside the scripts that are inside of my mini-game scene, or in the scripts carried into the scene by my GameController, but that was not the case. The problem laid in a script attached to an object located in a scene from which I moved to the mini-game.
Here if the troublesome line:
Physics2D.gravity = Vector2.zero;
Because my game is 2D with Axonometric projection (3/4) I set the gravity to Vector2.zero very early into the development. I have replaced that with simply setting the gravity scale to 0.
rb.gravityScale = 0;
Even though the script with the troublesome line doesn't move to my mini-game scene it modified the gravity setting of the entire project.
VERY silly mistake, which I probably would find, but after many hours of constant development my brain wasn't functioning too well, and I decided to post this question.
Thanks for the help anyway guys!
I am trying to make a 2d plat-former where you see the player from the side. I want him to be continuously moving and you have to press space at the right time so he doesn't fall. Right now everything works but he doesn't collide with the ground. I want it to be like he's running behind a wall so I want to ignore a certain layer I have made and collide with the boxes below that. So far I have tried ray casting, watched multiple tutorials, and did box collisions. Box collisions worked but to get all the platforms counted as solid I'd need like 50 box colliders. Here is my current code:
public int playerSpeed = 10;
public int playerJumpPower = 1250;
public float moveX;
public float playerYSize = 2;
public LayerMask mainGround;
public float playerFallSpeed = 5;
void Awake(){
}
// Update is called once per frame
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(10, 0));
if(hit.distance < 0.7f){
print("hi");
}
Vector3 characterTargetPosition = new Vector3(transform.position.x + playerSpeed, transform.position.y, transform.position.z);
transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerSpeed * Time.deltaTime);
if(Input.GetKeyDown("space")){
// float playerTargetPosY = transform.position.y + playerJumpPower;
// Vector3 characterTargetPosition = new Vector3(transform.position.x, playerTargetPosY, transform.position.z);
// transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerJumpPower * Time.deltaTime);
gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
}
//PlayerMove();
}
I have a rigidBody2D on my player so right now he just falls through the ground but the jump does work. If there is any easy way to do this. Like some script, a tutorial, or website I'm open for it. Please help.
Do you have a Rigidbody2D in your player? Things that will move usually have to have a RigidBody
(sorry for posting this as an answer. Cant comment yet)
EDIT:
try this:
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
//Physics usually are done in FixedUpdate to be more constant
public void FixedUpdate(){
if (Input.GetKeyDown("space"))
{
if(!rb.simulated)
//player can fall
rb.simulated = true;
rb.AddForce(Vector2.up * playerJumpPower);
}
else
{
//third argument is the distance from the center of the object where it will collide
//therefore you want the distance from the center to the bottom of the sprite
//which is half of the player height if the center is actually in the center of the sprite
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, playerYSize / 2);
if (hit.collider)
{
//make player stop falling
rb.simulated = false;
}
}
}
If the player is the only thing that will collide with something you can just take out the colliders from the object that the player will not collide with.
Else you can check for the layer of the collided object with hit.collider.gameObject.layer and decide if the player will collide with that layer or not
(note that you have to compare with the index of the layer. If you want to get the index by its name you can use LayerMask.NameToLayer(/*layer name*/))
you will have to do rb.simulated = true everytime you want to do something with the RigidBody (like AddForce())
hope it helped :)
I have a really odd issue, I created custom MOB AI in unity for my game (it has procedural generated voxel world so agents didn't work well). I use rigidbody on the mobs which I move around.
But I have this issue where mobs go inside the floor while moving (doesn't happen when standing) and when they stand, they teleport back up!. It's not animation, I disabled animations and it still happens.
here is how I move them:
private void WalkToTarget()
{
if (goal != Vector3.zero)
{
if (goal.y <= 5)
{
currentstatus = MOBSTATUS.STANDING;
return;
}
var distance = Vector3.Distance(VoxelPlayPlayer.instance.transform.position, gameObject.transform.position);
if (distance < 15)
{
goal = VoxelPlayPlayer.instance.transform.position;
goal.y = VoxelPlayEnvironment.instance.GetHeight(goal);
}
if (distance < 5)
{
this.currentstatus = MOBSTATUS.ATTACKING;
}
//MOVEMENT HAPPENS HERE
Vector3 direction = (goal - mobcontroller.transform.position).normalized*2f;
if(mobcontroller.collisionFlags!=CollisionFlags.CollidedBelow)
direction+= Vector3.down;
mobcontroller.Move(direction * Time.fixedDeltaTime);
RotateTowards(direction);
}
}
Edit:
All code: https://pastebin.com/khCmfKGi
Part of your problem is that you are using CollisionFlags incorrectly.
Instead of this:
if(mobcontroller.collisionFlags!=CollisionFlags.CollidedBelow)
You need to do this
if(mobcontroller.collisionFlags & CollisionFlags.CollidedBelow)
Because you are trying to check if the mob is at least colliding below, not if the mob is only colliding below.
Even then, CharacterController.Move should not move you through colliders on its own.
I suspect that RotateTowards(direction) might be rotating the boundaries of mob's collider through the ground in some cases. To prevent that, I recommend creating a lookDirection that keeps the character rotation flat when you do your RotateTowards:
Vector3 direction = (goal - mobcontroller.transform.position).normalized*2f;
if(mobcontroller.collisionFlags & CollisionFlags.CollidedBelow)
direction+= Vector3.down;
mobcontroller.Move(direction * Time.fixedDeltaTime);
Vector3 lookDirection = (goal - mobController.transform.position);
lookDirection.y = mobController.transform.y;
RotateTowards(lookDirection);
This problem happened when using Rigidbody and CharacterController on the mob. Removing Rigidbody from the mob solved this problem.
I have a blockbreaker type game coded using C# that mainly works fine but upon play testing i have found the ball will slow right down in certain situations! i.e if it wedges between to game object bricks the force of the ball rapidly slows down! 8 times out of ten this will not happen but other times it does and im unsure why! i will post the Ball script i think you will need to help solve this but should you need anymore information then please ask.
public class Ball : MonoBehaviour {
private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
void Start () {
paddle = GameObject.FindObjectOfType<Paddle> ();
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if (!hasStarted) {
//lock ball relative to the paddle
this.transform.position = paddle.transform.position + paddleToBallVector;
//wait for mouse press to start
if (Input.GetMouseButtonDown (0)) {
//if (Input.GetTouch(0).phase == TouchPhase.Ended){
hasStarted = true;
this.GetComponent<Rigidbody2D> ().velocity = new Vector2 (2f, 10f);
}
}
}
void OnCollisionEnter2D(Collision2D collision){
Vector2 tweak = new Vector2 (Random.Range(0f,0.2f),Random.Range(0f,0.2f));
if (hasStarted) {
GetComponent<AudioSource> ().Play ();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
}
You are adding directly to the velocity of the ball. The velocity variable defines a direction and speed, not just a speed as you are thinking here.
So, when the ball collides with a block it has a velocity of (+x, +y). After the collision and bounce is has a velocity of (+x, -y). So by adding a random positive value to the velocity when it has the -y velocity means that it will slow down the ball.
This does not happen every time because you are moving the ball in your Update() method. Change that to 'FixedUpdated()'. Fixed Update handles all physics calculations.
Also, I would recommend having a RigidBody2D on your Ball object. Moving physics objects should always have RigidBodies. With this, you can then use AddForce in the forward direction of your ball and that should solve your problem.
EDIT: I see you have a RigidBody2D already. I missed that the first time. Instead of having GetComponent<RigidBody2D>().velocity try GetComponent<RigidBody2D>().AddForce( transform.forward * impulseAmount, ForceMode.Impluse );