I'm starting to learn C# with Unity and I guess my code present some issues, the movement of my player works but not that good. Let me explain, I have a player at the bottom of the screen, moving only from left to right by itself, using transform.translate (I didn't used rigidbody) because on collision with the sides it stops, and is not a constant movement, the idea is that, once collides with the sides, it changes of direction, but same velocity.
The issue happens when the player colides with the walls (removed the mesh renderer to keep them transparent) but if at the same time you press the key to trigger also the change of direction the player gets stuck at the corner, as shown on this gif example:
Wait for the Player to get stuck at the corners, the gif takes like 15 secs.
And here's my actual Script for the Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PMovement : MonoBehaviour
{
public float playerSpeed = 8.0f;
void Update()
{
float moveX = playerSpeed * Time.deltaTime;
transform.Translate(moveX, 0, 0);
if (Input.GetKeyUp(KeyCode.Space))
{
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionEnter(Collision target)
{
if (target.gameObject.tag.Equals("SideWalls") == true)
{
playerSpeed = playerSpeed * -1;
}
}
}
Your code looks ok for what you want to achieve. Unfortunately, in Unity it may not be enough to pin point the problem, as the code heavily depends on the setting from the editor. One possible solution to this or similar problems:
If you still have blocking colliders, (The collision functions need some sort of rigidbody.), or if you clamp your player's position, to stop overflow, the following can happen:
Collider on right side is hit
Player direction change to left
Space is hit while colliders are still touching
Player direction change to right again
There is no collider on right side left to start the colliderEnter event again.
In this case block your space recognition while the player is touching one side.
If you change direction when you hit the wall, but you press Space at just the right time, you'd end up inside the wall a bit most likely. Then it'd keep thinking it was entering the wall collision and reversing direction over and over. Here's what I'd do.
public class PMovement : MonoBehaviour
{
private List<GameObject> Collisions = new List<GameObject>();
public float playerSpeed = 8.0f;
void Update()
{
float moveX = playerSpeed * Time.deltaTime;
transform.Translate(moveX, 0, 0);
if (Collisions.Count <= 0 && Input.GetKeyUp(KeyCode.Space))
{
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionEnter(Collision target)
{
if (!Collisions.Contains(target.gameObject) && target.gameObject.tag.Equals("SideWalls") == true)
{
Collisions.Add(target.gameObject);
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionExit(Collision target)
{
if (Collisions.Contains(target.gameObject)) {
Collisions.Remove(target.gameObject);
}
}
}
By keeping a list of wall objects you're inside, you can disallow the space bar if you're inside a wall. Keep in mind there's probably better ways to do the things you're doing, but this should fix the issue you're having for now.
Related
I'm making a 3D Side-Scroll Platformer Game,
I have trouble with my character when it steps on the moving platform it will not come along on the platform. I want my character to stay on the moving platform so I think converting my Character Controller into Rigibody will help me,
I need help to give me ideas on how I can reuse my Character Controller Script in Rigibody. This is my code, how can I reuse this in Rigibody script?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public CharacterController controller;
private Vector3 direction;
public float speed = 8;
public float jumpForce = 30;
public float gravity = -20;
public Transform groundCheck;
public LayerMask groundLayer;
public bool ableToMakeADoubleJump = true;
public Animator animator;
public Transform model;
void Start()
{
}
// Update is called once per frame
void Update()
{
if (PlayerManager.gameOver)
{
//play death animation
animator.SetTrigger("die");
//disable the script
this.enabled = false;
}
float hInput = Input.GetAxis("Horizontal");
direction.x = hInput * speed;
animator.SetFloat("speed", Mathf.Abs(hInput));
bool isGrounded = Physics.CheckSphere(groundCheck.position, 0.15f, groundLayer);
animator.SetBool("isGrounded", isGrounded);
if (isGrounded)
{
//Jump Codes
ableToMakeADoubleJump = true;
if (Input.GetButtonDown("Jump"))
{
Jump();
}
}
else
{
direction.y += gravity * Time.deltaTime;
if (ableToMakeADoubleJump & Input.GetButtonDown("Jump"))
{
DoubleJump();
}
}
if (hInput != 0)
{
Quaternion newRoattion = Quaternion.LookRotation(new Vector3(hInput, 0, 0));
model.rotation = newRoattion;
}
//Move the player using the character controller
controller.Move(direction * Time.deltaTime);
}
private void DoubleJump()
{
//Double Jump Codes
animator.SetTrigger("doubleJump");
direction.y = jumpForce;
ableToMakeADoubleJump = false;
}
private void Jump()
{
direction.y = jumpForce;
}
}
I would not recommend switching between the two. It would get tricky, and think about it, you are alternating between two very different things. One is movement and one is physics.
However, I would reccomend adding to your current script so that the player would move with the moving platform.
There is a lot of stuff in this answer, so read the whole thing.
Btw, when I talk about velocity, in your case it is direction.
Since it seems like you know how to code pretty well, I won’t write out the script, rather tell you some physics ideas to get you going in the right direction.
The reason people can stand on a moving platform and not fall off is because of friction.
If you are standing on a gameObject with enough friction (you could add a physics material the gameObject you stand on and change friction there. Note that physics materials only work with rigidbodies, but you might want to use it to just read the value)
First of all, you are going to want to raycast down to obtain the object you are standing on. From there you can get the physics material from hit.collider.sharedMaterial (or any other hit. to obtain data about what object you are standing on.
If they friction is too low, just make the character slip off, like it was before (I assume)
If the friction is above a threshold, get the velocity from the object you are standing on. If it was a rigidbody, hit.rigidbody.velocity. If it is controlled by script, use hit.collider.gameObject.GetComponent<scriptname>().velocityvariablename This part is continued later on
This is not necessary but useful: You can think of this as grabbing on a rope. When you are grabbing on a slippery rope, and someone pulls it (Like tug of war), You won’t move because the rope will slide through your hands. If the rope had grip tape on it and someone pulled it, you would come with it because it has more friction. You can think of the platform the same way. Now on to the more complex part: When you grip a rope that is stationary, and someone pulls it, you come with it as its velocity changes. When the rope is already being pulled, so its velocity is not stationary and it is already something. You grab onto it and a similar thing happens. It is like you are becoming a part of that rope. Similar to how if you are running, the arms and legs and head is a part of you. If you lose grip, you are no longer a part of that body, like your arms falling off when running. In other words, you become part of the body when you attach yourself to it.
Bottom line:
Get the velocity of the platform and set platformVel to it, do not add that to velocity, rather do a seperate controller.Move(platformVel).
A small customization:
Vector3.Lerp the platformVel to 0, so it doesn’t change while on the platform, but gradually goes to (0,0,0) when you get off. This way, there is a little momentum maintained from standing on the platform.
Feel free to ask anything in the comments.
I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}
I have a weird problem and cant seem to fix it. I was creating my first game in Unity and I was testing around a bit after creating a movement system and whenever I touch another object (it doesn't matter if it has a rigid body or not), my player suddenly starts moving on its own. I have a video showing what exactly happens: https://youtu.be/WGrJ0KNYSr4
I have tried a few different things already and I determined that it has to do something with the physics engine because it only happens when the player isn't kinematic. So, I tried to increase the solver iterations for physics in the project settings, but the bug was still happening. I looked for answers on the internet, yet the only thing I found was to remove Time.deltaTime, though it still doesn't work. I have found that it seems to happen less though when the player is moving fast.
I would really appreciate if somebody could help me with this. This is my first real game and I'm creating it for the Seajam that's happening on itch.io.
Here is the code for my playercontroller script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float playerSpeed = 3f;
private Rigidbody playerRb;
public GameObject cameraRotation;
// Start is called before the first frame update
void Start()
{
playerRb = GetComponent<Rigidbody>();
}
float verticalSpeed;
float horizontalSpeed;
bool isOnGround = true;
// Update is called once per frame
void Update()
{
//get controlled forward and backward motion
verticalSpeed = Input.GetAxis("Vertical");
transform.Translate(Vector3.forward * playerSpeed * verticalSpeed * Time.deltaTime);
//get controlled sidewards motion
horizontalSpeed = Input.GetAxis("Horizontal");
transform.Translate(Vector3.right * playerSpeed * horizontalSpeed * Time.deltaTime);
//lock the rotation of the player on the z and x axis
transform.eulerAngles = new Vector3(0, cameraRotation.transform.eulerAngles.y, 0);
//when pressing space jump and prevent air jump
if (Input.GetKeyDown(KeyCode.Space) && isOnGround)
{
playerRb.AddForce(Vector3.up * 10, ForceMode.Impulse);
isOnGround = false;
}
}
//check if the player is on the ground
private void OnCollisionEnter(Collision collision)
{
isOnGround = true;
}
}
Try not locking the player's rotation by script, maybe it is causing the problem due to gimbal lock. Instead go to your player's RigidBody->Constraints and then lock it.
You can read more about it here https://fr.wikipedia.org/wiki/Blocage_de_cardan
I'm trying to change the direction of the enemy when collides with a wall or the character. However, it doesn't work for the wall that the enemy bumps into, but only stays where it is.
Here's what I mean:
In this picture, the goomba switches the direction when bumps into Mario, however the same does not happen when the wall is hit.
What happens instead is this:
The Goomba stands still.
Here's the code of the goomba:
public class GoombaController : MonoBehaviour
{
public float speed = 1f;
private Vector2 force = Vector2.left;
void Update()
{
transform.Translate(force * Time.deltaTime * speed);
}
void OnCollisionEnter2D(Collision2D coll) {
if ((this.transform.position.x - coll.collider.transform.position.x) < 0) {
force = Vector2.left;
} else if ((this.transform.position.x - coll.collider.transform.position.x) > 0) {
force = Vector2.right;
}
}
}
Also, I use a tile map to generate the level, and the ground and the walls are on the same tile map.
If anything else is needed as info, just leave a comment below the question.
How can I solve this?
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)