i have problem with blocking movement i already read question on Blocking Movement On Collision
but i dont have any idea for my own problem.
if you can give me the logic i will try to solve my own problem.
i hope you can help me
this my player class update
:EDIT: thanks for Kai Hartmann for reply
my problem is for 2D graphic and i want to know how to stop movement when object1 collision with object2
public void Update(GameTime gameTime)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Up;
velocity.Y = -3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Down;
velocity.Y = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Right;
velocity.X = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
currentFace = FacePosition.Left;
velocity.X = -3;
}
else
{
//currentFace = FacePosition.Down;
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
and this my collision
public void IntersectWithScore(Vector2 vector1, Vector2 vector2, Texture2D object1, Texture2D object2, int doSomethingToScore, bool isIncrease)
{
if (vector1.X + object1.Width < vector2.X || vector1.X > vector2.X + object2.Width ||
vector1.Y + object1.Height < vector2.Y || vector1.Y > vector2.Y + object2.Height)
{
}
else
{
player1.Velocity = Vector2.Zero;
}
}
No need for a collision check Method as far as I'm concerned :) I've written this code really quickly but should work okay?(ish). I've assumed you want to check if something is in the player's way and also assumed that there are more than one objects that could be in your players way so to do this I suggest making a class for the 'Solid' object and giving it a position, width and height. Then make a list of this class to contain the multiple 'solid' objects.
// collidable object's Class name = SolidBlock
SolidBlock newSolidBlock;
List<SolidBlock> solidBlocksList = new List<SolidBlock>();
// This will create 10 'solid' objects and adds them all to a list
// This would preferably go in the Load() function so you can set the positions of
// the blocks after creating them (after the loop)
int i = 0;
while (i < 10)
{
newSolidBlock = new SolidBlock(Vector2, position) // Assuming width and height
// are set within class
solidBlocksList.Add(newSolidBlock);
i++;
}
// It doesn't matter how you create them, the important lines here are the creation of a
// newSolidBlock and then the line adding it to the list of blocks.
solidBlocksList[0].position = new Vector (20, 50); // < for example this is how you would set
// a block's position.
Then in your players update function you refer to this list of objects.
// Input a list of the objects you want your player to collide with
// this is a list in case you have multiple object2s you want to check
// for collision.
// An Object2 should have a position, height, and width, yes?
public void Update(GameTime gameTime, List<Object2> object2List)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y <= o.position.Y + o.height)
{
velocity.Y = 0;
}
else
{
velocity.Y = -3;
}
}
currentFace = FacePosition.Up;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y + playerWidth >= o.position.Y)
{
velocity.Y = 0;
}
else
{
velocity.Y = 3;
}
}
currentFace = FacePosition.Down;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.X + playerWidth >= o.position.X)
{
velocity.X = 0;
}
else
{
velocity.X = 3;
}
}
currentFace = FacePosition.Right;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
foreach (Object2 o in object2List)
{
if (position.X <= o.position.X + o.width)
{
velocity.X = 0;
}
else
{
velocity.X = -3;
}
}
currentFace = FacePosition.Left;
}
else
{
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
Sorry if this isn't much help, especially the first block of code, that was very much an example of how you could create multiple object2s.
Related
I'm new to using Unity and C# and I've been working on creating a movement system for a 2D platformer, and I've decided I want to switch to the new Unity Input System (because I can't find good information on the old one as all the search results I get are for the new one). I have it downloaded and installed and have watched a ton of videos but can't get any inputs to actually work in my game.
Here is the code which I need help translating:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class CharacterLogic : MonoBehaviour
{
private bool playerIsAlive = true;
Vector2 movementInput;
bool jumpInput;
bool doubleJumpInput;
private float sprintTimer;
private bool isSprinting;
private int jumpsRemaining = 1;
private bool facingRight = true;
private bool isGrounded;
private bool wallJumping = false;
[Header("Collision")]
public BoxCollider2D playerCollider;
public LayerMask jumpableGround;
[Header("Movement")]
public Rigidbody2D characterBody;
public float runSpeed = 7;
public float maxSpeed = 10;
public float jumpStrength = 11;
[Header("Camera Tracking")]
public CameraScript mainCamera;
public float zoomSpeed;
public float ZoomAmount;
public float maxZoomAmount;
[Header("Wall Jump System")]
public Transform frontCheck;
public float wallSlidingSpeed;
public float checkRadius;
public float xWallForce;
public float yWallForce;
public float wallJumpTime;
bool isTouchingFront;
bool wallSliding;
[Header("Animation Settings")]
public Animator animator;
private string currentState;
//Animation States
const string PLAYER_IDLE = "Idle";
const string PLAYER_RUN = "Running";
const string PLAYER_SPRINT = "Sprinting";
const string PLAYER_JUMP = "Jumping";
const string PLAYER_WALLSLIDE = "Wallslide";
[Header("Inputs")]
public PlayerInput playerInput;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
movementInput.x = Input.GetAxisRaw("Horizontal");
movementInput.y = Input.GetAxisRaw("Vertical");
isGrounded = Physics2D.BoxCast(playerCollider.bounds.center, playerCollider.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
if (Input.GetButtonDown("Jump"))
{
if (!isGrounded)
{
if (wallSliding)
{
jumpsRemaining += -1;
}else if (jumpsRemaining > 0)
{
jumpInput = true;
doubleJumpInput = true;
}
}
else if (isGrounded)
{
jumpInput = true;
}
// Wall jump
if (!facingRight)
{
if (movementInput.x > 0 && wallSliding == true)
{
wallJumping = true;
}
}else if (facingRight)
{
if (movementInput.x < 0 && wallSliding == true)
{
wallJumping = true;
}
}
}
}
void FixedUpdate()
{
// Flip the character sprite's direction
if (facingRight == false && movementInput.x > 0 && !wallSliding && (isGrounded || doubleJumpInput))
{
Flip();
} else if (facingRight == true && movementInput.x < 0 && !wallSliding && (isGrounded || doubleJumpInput))
{
Flip();
}
// Jump
if (jumpInput && playerIsAlive && wallSliding != true)
{
Jump();
}
// Run
if (movementInput.x != 0 && playerIsAlive && wallSliding == false)
{
Run();
if (characterBody.velocity.x == maxSpeed)
{
mainCamera.xOffset = 6;
//mainCamera.ZoomOut(maxZoomAmount, zoomSpeed);
}else if (characterBody.velocity.x == -maxSpeed)
{
mainCamera.xOffset = -6;
}
else
{
if (movementInput.x > 0)
{
mainCamera.xOffset = 4;
}else if (movementInput.x < 0)
{
mainCamera.xOffset = -4;
}
}
}
// Idle
if (((movementInput.x == 0) && (movementInput.y == 0)) && playerIsAlive && wallSliding == false)
{
Idle();
ChangeAnimationState(PLAYER_IDLE);
}
// Reset sprint timer
if (movementInput.x == 0)
{
sprintTimer = 0;
mainCamera.xOffset = 0;
}
// Reset double jump
if (isGrounded)
{
jumpsRemaining = 1;
}
// Detect wall collisions
isTouchingFront = Physics2D.OverlapCircle(frontCheck.position, checkRadius, jumpableGround);
if (isTouchingFront == true && !isGrounded && Input.GetButton("Fire1"))
{
wallSliding = true;
}
else
{
wallSliding = false;
}
if (wallSliding && !wallJumping)
{
WallSlide();
ChangeAnimationState(PLAYER_WALLSLIDE);
}
if (wallJumping == true)
{
ChangeAnimationState(PLAYER_JUMP);
Invoke("SetWallJumpingToFalse", wallJumpTime);
characterBody.velocity = new Vector2(xWallForce * movementInput.x, yWallForce);
Flip();
}
//Debug.Log(jumpsRemaining);
}
/// <summary>
/// This function sets the character's velocity in the Y axis to the value of `jumpStrength`,.
/// It also preserves the character's current velocity along the Y axis.
/// </summary>
public void Jump()
{
//Debug.Log("Normal Jump");
jumpsRemaining += -1;
Vector2 velocity = characterBody.velocity;
if ((velocity.x > 0 && movementInput.x > 0) || (velocity.x < 0 && movementInput.x < 0))
{
}
else
{
if (movementInput.x > 0)
{
velocity.x = 7;
}
else if (movementInput.x < 0)
{
velocity.x = -7;
}
}
velocity.y = jumpStrength;
characterBody.velocity = velocity;
jumpInput = false;
if (doubleJumpInput == true)
{
doubleJumpInput = false;
}
if (!isGrounded)
{
ChangeAnimationState(PLAYER_JUMP);
}
}
/// <summary>
/// This function sets the character's velocity in the X axis to the value of `runSpeed`.
/// It also preserves the character's current velocity along the Y axis.
/// </summary>
public void Run()
{
sprintTimer += Time.deltaTime;
Vector2 velocity = characterBody.velocity;
if (sprintTimer > 2 && characterBody.velocity.x != 0)
{
if (isGrounded)
{
if (movementInput.x > 0)
{
velocity.x = maxSpeed;
isSprinting = true;
ChangeAnimationState(PLAYER_SPRINT);
}
else if (movementInput.x < 0)
{
velocity.x = -maxSpeed;
isSprinting = true;
ChangeAnimationState(PLAYER_SPRINT);
}
} else
{
if (isSprinting == false)
{
if (velocity.x > 0)
{
velocity.x = runSpeed;
isSprinting = false;
ChangeAnimationState(PLAYER_RUN);
}
else if (velocity.x < 0)
{
velocity.x = -runSpeed;
isSprinting = false;
ChangeAnimationState(PLAYER_RUN);
}
}
}
}
else
{
if (movementInput.x > 0 && isGrounded)
{
velocity.x = runSpeed;
isSprinting = false;
ChangeAnimationState(PLAYER_RUN);
}
else if (movementInput.x < 0 && isGrounded)
{
velocity.x = -runSpeed;
isSprinting = false;
ChangeAnimationState(PLAYER_RUN);
}
}
characterBody.velocity = velocity;
}
public void Idle()
{
characterBody.velocity = new Vector2(0, characterBody.velocity.y);
}
private void Flip()
{
facingRight = !facingRight;
Vector3 scaler = transform.localScale;
scaler.x *= -1;
transform.localScale = scaler;
}
private void WallSlide()
{
//Flip();
characterBody.velocity = new Vector2(characterBody.velocity.x, Mathf.Clamp(characterBody.velocity.y, -wallSlidingSpeed, float.MaxValue));
}
private void ChangeAnimationState(string newState)
{
// Stop the same animation from interrupting itself
if (currentState == newState) return;
//Play the animation
animator.Play(newState);
// Reassign the current state to the new state
currentState = newState;
}
private void SetWallJumpingToFalse()
{
wallJumping = false;
jumpsRemaining = 1;
}
public void OnJump(InputAction.CallbackContext value)
{
Debug.Log("Jump");
}
public void OnMovement(InputAction.CallbackContext value)
{
Vector2 inputMovement = value.ReadValue<Vector2>();
Debug.Log("Jump");
}
}
If anyone can help dumb this down for me I'd be eternally grateful. As you can see from the last two functions, I am trying to trigger unity events, assign the events to functions and write to the Debug.log so that I can see it's working. For instance currently, it should say "Jump" when I press the button I've mapped to "Jump" in the action map, but in reality it just triggers the old input manager stuff normally and the new stuff does nothing.
I have the PlayerInput class correctly attached to the CharacterLogic script in the inspector, and I believe everything is set up correctly with the events calling the right action map and function from my code (screenshot attached)
Help?
I have tried following every guide I could see on youtube and they were overly complicated and I couldn't see how I could translate how my old input system was working over to their methods.
Yeah I figured out the actual issue. When you create a control scheme, you are given the "option" to add a requirement. It's not optional, if you don't add a requirement, it won't work. That should really be made a mandatory field.
I'm making a 2D platform fighter but I've looked all over the internet and I'm not sure i can find what I'm looking for. I'm trying to make a melee attack to when you hit R-click on the mouse it dashes in the direction you are walking.
Ill try and figure out knock-back or dmg later. Any help with the movement of the script?
I'll list my current movement code below.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace NinjaController {
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
public class NinjaController : MonoBehaviour {
private Rigidbody2D RBody { get; set; }
[SerializeField]
private PhysicsParams physicsParams;
public Vector2 Velocity { get { return(RBody.velocity); } }
public Vector2 VelocityRelativeGround { get { return(Velocity / PhysicsParams.onGroundMaxVelHorizontal); } }
private float timeRealLastGroundCollision = 0;
private float timeRealLastWallLeftCollision = 0;
private float timeRealLastWallRightCollision = 0;
public bool IsOnGround {
get {
return GetIsColliding(timeRealLastGroundCollision);
}
}
public bool IsOnWallLeft {
get {
return GetIsColliding(timeRealLastWallLeftCollision);
}
}
public bool IsOnWallRight {
get {
return GetIsColliding(timeRealLastWallRightCollision);
}
}
public bool IsInAir { get { return isPlayerInAir; } }
private bool GetIsColliding(float timeLastCollision) {
return(Time.realtimeSinceStartup < timeLastCollision + 0.05f);
}
private Vector2 currentVelocity = Vector2.zero;
private Vector2 currentForce = Vector2.zero;
private float EntityMass { get { return(PhysicsParams.playerMass); } }
private bool isPlayerInAir = false;
private bool keyJumpRetrigger = false;
private bool keyJumpPressed = false;
private bool isPlayerOnWall = false;
public PhysicsParams PhysicsParams {
get { return physicsParams; }
set { physicsParams = value; }
}
public Vector2 CurrentForce { get { return currentForce; } }
public bool IsOnWall { get { return isPlayerOnWall; } }
private List<Renderer> allRenderers;
public List<Renderer> AllRenderers { get { return allRenderers; } }
public Vector3 Position {
get {
return transform.position;
}
set {
transform.position = value;
}
}
public Vector2 Position2D {
get {
return transform.position;
}
set {
transform.position = value;
}
}
public void Awake() {
RBody = GetComponent<Rigidbody2D>();
allRenderers = new List<Renderer>(GetComponentsInChildren<Renderer>(true));
}
public void Update() {
//let's reset forces to 0 and then add regular gravitation
SimResetForce();
SimAddForce(new Vector2(0, PhysicsParams.gameGravity) * EntityMass);
//process key input (like jumping key pressed, etc...)
ProcessInput();
//simulate position and velocity based on all acting forces
ComputeVelocity(Time.deltaTime);
//collision detection with static world
isPlayerOnWall = IsOnWallLeft || IsOnWallRight;
isPlayerInAir = IsOnGround == false;
}
private void SimResetForce() {
currentForce = Vector2.zero;
}
private void SimAddForce(Vector2 force) {
currentForce += force;
}
private void ComputeVelocity(float dt) {
currentVelocity += (currentForce / EntityMass) * dt;
//let's cap the speed in case its higher than the max
if (isPlayerInAir) {
currentVelocity.x = Mathf.Clamp(currentVelocity.x, -PhysicsParams.inAirMaxVelHorizontal, PhysicsParams.inAirMaxVelHorizontal);
} else {
currentVelocity.x = Mathf.Clamp(currentVelocity.x, -PhysicsParams.onGroundMaxVelHorizontal, PhysicsParams.onGroundMaxVelHorizontal);
}
RBody.velocity = currentVelocity;
}
private void ProcessInput() {
bool isKeyDownJump = Input.GetButton("Jump");
float inputAxisX = Input.GetAxisRaw("Horizontal");
bool isKeyDownLeft = inputAxisX < -0.5f;
bool isKeyDownRight = inputAxisX > 0.5f;
//-----------------
//JUMPING LOGIC:
//player is on ground
if (isPlayerInAir == false) {
//in case the player is on ground and does not press the jump key, he
//should be allowed to jump
if (isKeyDownJump == false) {
keyJumpRetrigger = true;
}
//did player press down the jump button?
if (isKeyDownJump == true && keyJumpRetrigger == true) {
keyJumpPressed = true;
keyJumpRetrigger = false;
//when pressing jump on ground we set the upwards velocity directly
currentVelocity = new Vector2(currentVelocity.x, PhysicsParams.jumpUpVel);
}
} else if (isPlayerOnWall == true) {
//let's allow jumping again in case of being on the wall
if (isKeyDownJump == false) {
keyJumpRetrigger = true;
}
if (currentVelocity.y < 0) {//apply friction when moving downwards
SimAddForce(new Vector2(0, PhysicsParams.wallFriction) * EntityMass);
}
if (currentVelocity.y < PhysicsParams.wallFrictionStrongVelThreshold) {//apply even more friction when moving downwards fast
SimAddForce(new Vector2(0, PhysicsParams.wallFrictionStrong) * EntityMass);
}
if (isKeyDownJump == true && keyJumpRetrigger == true) {
keyJumpPressed = true;
keyJumpRetrigger = false;
//in case we are moving down -> let's set the velocity directly
//in case we are moving up -> sum up velocity
if (IsOnWallLeft == true) {
if (currentVelocity.y <= 0) {
currentVelocity = new Vector2(PhysicsParams.jumpWallVelHorizontal, PhysicsParams.jumpWallVelVertical);
} else {
currentVelocity = new Vector2(PhysicsParams.jumpWallVelHorizontal, currentVelocity.y + PhysicsParams.jumpWallVelVertical);
}
} else if (IsOnWallRight == true) {
if (currentVelocity.y <= 0)
currentVelocity = new Vector2(-PhysicsParams.jumpWallVelHorizontal, PhysicsParams.jumpWallVelVertical);
else
currentVelocity = new Vector2(-PhysicsParams.jumpWallVelHorizontal, currentVelocity.y + PhysicsParams.jumpWallVelVertical);
}
}
}
//did player lift the jump button?
if (isKeyDownJump == false) {
keyJumpPressed = false;
}
//let's apply force in case we are holding the jump key during a jump.
if (keyJumpPressed == true) {
SimAddForce(new Vector2(0, PhysicsParams.jumpUpForce) * EntityMass);
}
//however let's stop doing that as soon as we fall down after the up-phase.
if (keyJumpPressed == true && currentVelocity.y <= 0) {
keyJumpPressed = false;
}
//let's apply additional gravity in case we're in air moving up but not holding the jump button
if (keyJumpPressed == false && isPlayerInAir == true && currentVelocity.y > 0) {
SimAddForce(new Vector2(0, PhysicsParams.jumpGravity) * EntityMass);
}
//-----------------
//IN AIR SIDEWAYS:
if (isPlayerInAir == true) {
//steering into moving direction (slow accel)
if (isKeyDownLeft == true && currentVelocity.x <= 0)
SimAddForce(new Vector2(-PhysicsParams.inAirMoveHorizontalForce, 0) * EntityMass);
else if (isKeyDownRight == true && currentVelocity.x >= 0)
SimAddForce(new Vector2(PhysicsParams.inAirMoveHorizontalForce, 0) * EntityMass);
//steering against moving direction (fast reverse accel)
else if (isKeyDownLeft == true && currentVelocity.x >= 0)
SimAddForce(new Vector2(-PhysicsParams.inAirMoveHorizontalForceReverse, 0) * EntityMass);
else if (isKeyDownRight == true && currentVelocity.x <= 0)
SimAddForce(new Vector2(PhysicsParams.inAirMoveHorizontalForceReverse, 0) * EntityMass);
}
//-----------------
//ON GROUND SIDEWAYS:
if (isPlayerInAir == false) {
//steering into moving direction (slow accel)
if (isKeyDownLeft == true && currentVelocity.x <= 0)
SimAddForce(new Vector2(-PhysicsParams.onGroundMoveHorizontalForce, 0) * EntityMass);
else if (isKeyDownRight == true && currentVelocity.x >= 0)
SimAddForce(new Vector2(PhysicsParams.onGroundMoveHorizontalForce, 0) * EntityMass);
//steering against moving direction (fast reverse accel)
else if (isKeyDownLeft == true && currentVelocity.x >= 0)
SimAddForce(new Vector2(-PhysicsParams.onGroundMoveHorizontalForceReverse, 0) * EntityMass);
else if (isKeyDownRight == true && currentVelocity.x <= 0)
SimAddForce(new Vector2(PhysicsParams.onGroundMoveHorizontalForceReverse, 0) * EntityMass);
//not steering -> brake due to friction.
else if (isKeyDownLeft != true && isKeyDownRight != true && currentVelocity.x > 0)
SimAddForce(new Vector2(-PhysicsParams.groundFriction, 0) * EntityMass);
else if (isKeyDownLeft != true && isKeyDownRight != true && currentVelocity.x < 0)
SimAddForce(new Vector2(PhysicsParams.groundFriction, 0) * EntityMass);
//in case the velocity is close to 0 and no keys are pressed we should make the the player stop.
//to do this let's first undo the prior friction force, and then set the velocity to 0.
if (isKeyDownLeft != true && isKeyDownRight != true && currentVelocity.x > 0 && currentVelocity.x < PhysicsParams.groundFrictionEpsilon) {
SimAddForce(new Vector2(PhysicsParams.groundFriction, 0) * EntityMass);
currentVelocity.x = 0;
} else if (isKeyDownLeft != true && isKeyDownRight != true && currentVelocity.x < 0 && currentVelocity.x > -PhysicsParams.groundFrictionEpsilon) {
SimAddForce(new Vector2(-PhysicsParams.groundFriction, 0) * EntityMass);
currentVelocity.x = 0;
}
}
}
public void ResetVelocity() {
currentVelocity = Vector2.zero;
}
public void OnCollisionStay2D(Collision2D collision) {
foreach (ContactPoint2D contactPoint in collision.contacts) {
if (GetIsVectorClose(new Vector2(0, 1), contactPoint.normal)) {
timeRealLastGroundCollision = Time.realtimeSinceStartup;
currentVelocity.y = Mathf.Clamp(currentVelocity.y, 0, Mathf.Abs(currentVelocity.y));
}
if (GetIsVectorClose(new Vector2(1, 0), contactPoint.normal)) {
timeRealLastWallLeftCollision = Time.realtimeSinceStartup;
currentVelocity.x = Mathf.Clamp(currentVelocity.x, 0, Mathf.Abs(currentVelocity.x));
}
if (GetIsVectorClose(new Vector2(-1, 0), contactPoint.normal)) {
timeRealLastWallRightCollision = Time.realtimeSinceStartup;
currentVelocity.x = Mathf.Clamp(currentVelocity.x, -Mathf.Abs(currentVelocity.x), 0);
}
if(GetIsVectorClose(Vector2.down, contactPoint.normal)) {
currentVelocity.y = Mathf.Clamp(currentVelocity.y, -Mathf.Abs(currentVelocity.y), 0);
}
}
}
private bool GetIsVectorClose(Vector2 vectorA, Vector2 vectorB) {
return(Mathf.Approximately(0, Vector2.Distance(vectorA, vectorB)));
}
public void OnLifeChanged (int life, Vector2 contactVector) {
const float forceEnemyCollision = 15.0f;
currentVelocity = contactVector.normalized * forceEnemyCollision;
}
public void ResetPlayer() {
currentVelocity = Vector2.zero;
}
}
}
For dashing, it will be something along the line of:
bool isDashing;
float currentDashTime = 0; // How long in seconds since the dash started.
float dashTime = 1f; // How long in seconds will the dash be.
float dashSpeed = 10f;
private void Update()
{
if (Input.GetMouseButtonDown(1)) // Right click
{
if (!isDashing) StartCoroutine(Dash());
}
}
private IEnumerator Dash()
{
isDashing = true;
currentDashTime = 0;
Vector2 facingDirection = new Vector2(transform.right.x, transform.right.y)
rb2d.AddForce(facingDirection * dashSpeed);
while (currentDashTime < dashTime)
{
currentDashTime += Time.deltaTime;
yield return null;
}
// If you want the character to stop after dashing ends.
// rb.velocity = Vector2.zero;
isDashing = false;
}
As for melee attacks, I'll recommend looking into 2D Raycasts, provided your enemies have colliders on them. You can raycast before you start dashing so you know what you will collide with during the dash beforehand.
RaycastHit2D hit = Physics2D.Raycast(transform.position, facingDirection);
if (hit.collider != null)
{
// Check for enemy, damage the enemy... etc.
}
Use Physics2D.RaycastAll if you want to get all enemies hit along a line. Physics2D.RayCast only returns the first collider hit.
I've formulated my own collision system in a modified version of OpenTK. It works by running a foreach loop which checks all the quads in the game (this runs when something moves) and sets their position back to where it was last frame, that is if it intersects this frame. I might not have explained that well, so here is the SetXVelocity code, called when a player moves right.
public void SetXVelocity(float x)
{
foreach (Quad quad in quads)
{
if (!quad.Equals(this))
{
if (Intersects(quad))
{
Velocity.x = 0;
Position = OldPosition;
continue;
}
if (!Intersects(quad))
{
OldPosition = Position;
Velocity.x = x;
continue;
}
}
else
{
OldPosition = Position;
continue;
}
OldPosition = Position;
continue;
}
}
Here is the code for Intersects:
public bool Intersects(Quad quad)
{
#region
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if (BRCorner.x < quad.BRCorner.x && BRCorner.x > quad.TLCorner.x && BRCorner.y > quad.BRCorner.y && BRCorner.y < quad.TLCorner.y)
{
return true;
}
if (TRCorner.x < quad.TRCorner.x && TRCorner.x > quad.BLCorner.x && TRCorner.y < quad.TRCorner.y && TRCorner.y > quad.BLCorner.y)
{
return true;
}
if (BLCorner.x > quad.BLCorner.x && BLCorner.x < quad.TRCorner.x && BLCorner.y > quad.BLCorner.y && BLCorner.y < quad.TRCorner.y)
{
return true;
}
#endregion // Corner Intersection
if (Math.Round(Left, 2) == Math.Round(quad.Right, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
if (Math.Round(Right, 2) == Math.Round(quad.Left, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
return false;
}
By the way, TRCorner is a Vector3 representing the Top Right Corner etc.
Last block of code is the Quad class, keep in mind, the actual class is huge, so I'll try not include all of it:
public Vector3 Velocity;
public Vector3 Position;
public int Index;
public VBO<Vector3> VertexData
{
get;
set;
}
public VBO<int> VertexCount
{
get;
set;
}
public VBO<Vector2> VertexTexture
{
get;
set;
}
For some reason, a few quads have no collision and some do. Some even have collision that makes the player stick to them.
I think some quads are missing because all possible conditions are not covered in your comparisons.
Since you && all your conditions, a collision needs to meet all your conditions to get detected.
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if( TLCorner.x == quad.TLCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y ) the rectangle still collides, but undetected.
Same for other conditions.
You might want to use >= and <= in your code in order to cover all conditions.
As for why they get stuck in one another, your velocity probably pushes them into a condition which makes them collide forever, since you detect the collision after they went into each other and not when the boundaries overlap.
Also you might want to reverse the velocity and "un-stuck" them when collision is detected in a way that does not make them stuck in other nearby rectangles.
I highly recommend you use better logic for collision detection
Link: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
You can test it here http://jsfiddle.net/knam8/
I've been having some trouble applying correct velocity changes to my ball when it hits bricks in my Breakout clone. In a previous question, I was advised to use continuous collision detection, as well as other methods such as finding the intersection between the ball and the brick when it hits a corner to determine which direction the ball should reflect. I've applied this to my code below, but there are still occasions when the ball will just completely plow through a collection of bricks. This is more noticeable when it hits moving bricks.
In Level.cs Update method:
Bricks.ForEach(brick => Balls.ForEach(ball => ball.Collide(brick)));
In Ball.cs:
public bool Touching(Brick brick)
{
var position = Position + (Velocity * Speed);
return position.Y + Size.Y >= brick.Position.Y &&
position.Y <= brick.Position.Y + brick.Size.Y &&
position.X + Size.X >= brick.Position.X &&
position.X <= brick.Position.X + brick.Size.X && brick.Health > 0 && brick.Lifespan == 1F;
}
public void Collide(Brick brick)
{
if (!Touching(brick)) return;
var position = Position + (Velocity * Speed);
var bounds = new Rectangle((int)position.X, (int)position.Y, Texture.Width, Texture.Height);
var nonCCDBounds = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var change = new Vector2(Velocity.X, -Velocity.Y);
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width < intersection.Height || nonCCDIntersection.Width < nonCCDIntersection.Height){
change = new Vector2(-Velocity.X, Velocity.Y);
}
}
if (bounds.Intersects(brick.Top))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y - 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y > brick.Position.Y)
change = new Vector2(-Velocity.X, Velocity.Y);
}
if (bounds.Intersects(brick.Bottom))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y + 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y < brick.Position.Y + brick.Texture.Bounds.Height)
change = new Vector2(-Velocity.X, Velocity.Y);
}
ReflectBall(brick, change);
return;
}
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var change = new Vector2(-Velocity.X, Velocity.Y);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width > intersection.Height || nonCCDIntersection.Width > nonCCDIntersection.Height)
{
change = new Vector2(Velocity.X, -Velocity.Y);
}
}
if (bounds.Intersects(brick.Left))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X - 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X > brick.Position.X)
change = new Vector2(Velocity.X, -Velocity.Y);
}
if (bounds.Intersects(brick.Right))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X + 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X < brick.Position.X + brick.Texture.Bounds.Width)
change = new Vector2(Velocity.X, -Velocity.Y);
}
ReflectBall(brick, change);
}
}
public void ReflectBall(Brick brick, Vector2 reflection)
{
Position = Position - Velocity;
Velocity = reflection;
if (brick.Health < 9)
{
brick.Health--;
brick.Life --;
}
if (brick.Health > 0 && brick.Life > 0)
{
brick.Texture = Assets.GetBrick(brick.TextureName, brick.Health);
}
}
It's a bit of a mess but it's the closest I've got to having decent collision. It would be much easier if there was a fast way of finding out collision points and applying correct velocity changes.
When my AABB physics engine resolves an intersection, it does so by finding the axis where the penetration is smaller, then "push out" the entity on that axis.
Considering the "jumping moving left" example:
If velocityX is bigger than velocityY, AABB pushes the entity out on the Y axis, effectively stopping the jump (result: the player stops in mid-air).
If velocityX is smaller than velocitY (not shown in diagram), the program works as intended, because AABB pushes the entity out on the X axis.
How can I solve this problem?
Source code:
public void Update()
{
Position += Velocity;
Velocity += World.Gravity;
List<SSSPBody> toCheck = World.SpatialHash.GetNearbyItems(this);
for (int i = 0; i < toCheck.Count; i++)
{
SSSPBody body = toCheck[i];
body.Test.Color = Color.White;
if (body != this && body.Static)
{
float left = (body.CornerMin.X - CornerMax.X);
float right = (body.CornerMax.X - CornerMin.X);
float top = (body.CornerMin.Y - CornerMax.Y);
float bottom = (body.CornerMax.Y - CornerMin.Y);
if (SSSPUtils.AABBIsOverlapping(this, body))
{
body.Test.Color = Color.Yellow;
Vector2 overlapVector = SSSPUtils.AABBGetOverlapVector(left, right, top, bottom);
Position += overlapVector;
}
if (SSSPUtils.AABBIsCollidingTop(this, body))
{
if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
(Position.Y + Height/2f == body.Position.Y - body.Height/2f))
{
body.Test.Color = Color.Red;
Velocity = new Vector2(Velocity.X, 0);
}
}
}
}
}
public static bool AABBIsOverlapping(SSSPBody mBody1, SSSPBody mBody2)
{
if(mBody1.CornerMax.X <= mBody2.CornerMin.X || mBody1.CornerMin.X >= mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y <= mBody2.CornerMin.Y || mBody1.CornerMin.Y >= mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsColliding(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsCollidingTop(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
if(mBody1.CornerMax.Y == mBody2.CornerMin.Y)
return true;
return false;
}
public static Vector2 AABBGetOverlapVector(float mLeft, float mRight, float mTop, float mBottom)
{
Vector2 result = new Vector2(0, 0);
if ((mLeft > 0 || mRight < 0) || (mTop > 0 || mBottom < 0))
return result;
if (Math.Abs(mLeft) < mRight)
result.X = mLeft;
else
result.X = mRight;
if (Math.Abs(mTop) < mBottom)
result.Y = mTop;
else
result.Y = mBottom;
if (Math.Abs(result.X) < Math.Abs(result.Y))
result.Y = 0;
else
result.X = 0;
return result;
}
It's hard to read code of other people, but I think this is one possible (purely brainstormed) workaround, although of course I'm not able to test it:
Before the collision detection happens, save the players velocity to some temporary variable.
After you've done your collision response, check if the players X or Y position has been corrected
If X position has been changed, manually reset (as a kind of "safety reset") the players Y velocity to the one that he had before the response.
By the way, what happens with your current code when your players hits the roof while jumping?
I had that same problem. Even the Microsoft platformer starterskit seems to have that bug.
The solution I found so far is to either use multisampling (argh) or to use the movedirection of the object: only move the distance between those 2 objects and no further when a collision is detected (and do that for each axis).
One possible solution I found is sorting the objects before resolving based on the velocity of the player.