Problems with movement GameMaker studio 2 - game-maker

I am make a game for school, within this game I have an (hero)asset, this asset walks on keypresses and stops when I don't press the key.
The only problem I have, is when the I stop walking de animation keeps on running. In other words, it stands still, but it still walks.
This is my code:
/////////////////////
// MOVING //
/////////////////////
if(keyboard_check(vk_left)){ // moving left collisions
dir=-1; // set the correct direction
image_xscale = dir; // make the sprite face the correct direction
// if we are not jumping or falling
sprite_index = spr_denpman_Loop; // set the sprite to walking
x=x-xspeed // move the player left
c2 = -1;
c3 = -1;
// check the points at the bottom of the sprite
c1 = tilemap_get_at_pixel(obj_Spel.map,x-(sprite_get_width(sprite_index)/2),y-1); // left
c3 = tilemap_get_at_pixel(obj_Spel.map,x,y-1); // center
if( y&$3f>0 ) c2=tilemap_get_at_pixel(obj_Spel.map,x-(sprite_get_width(sprite_index)/2),y+1); // left below (only check if there is a tile below)
if(c1 == 3) || (c2 == 3){ // if we are intersecting with a box
x = real(x&$ffffffc0)+(sprite_get_width(sprite_index)/2); // stop the player from moving
}
if(x < 0){ // the the player has moved off the edge of the screen
x = room_width; // wrap around to the other side of the screen
}
}else if(keyboard_check(vk_right)){ // moving right collisions (check with else so that both directions cant be triggered at the same time)
dir=1; // set the correct direction
image_xscale = dir; // make the sprte face the correct direction
// if we are not jumping or falling
sprite_index = spr_denpman_Loop; // set the sprite to walking
image_speed = anim_speed;
x=x+xspeed; // move the player right
c2 = -1;
c3 = -1;
// check the points at the bottom of the sprite
c1 = tilemap_get_at_pixel(obj_Spel.map,x+(sprite_get_width(sprite_index)/2),y-1); // right
c3 = tilemap_get_at_pixel(obj_Spel.map,x,y-1); // center
if( y&$3f>0 ) c2=tilemap_get_at_pixel(obj_Spel.map,x+(sprite_get_width(sprite_index)/2),y+1); // right below (only check if there is a tile below)
if(c1 == 3) || (c2 == 3){ // if we are intersecting with a box
x = real(x&$ffffffc0)+obj_Spel.tilesize-(sprite_get_width(sprite_index)/2); // stop the player from moving
}
if(x > room_width){ // the the player has moved off the edge of the screen
x = 0; // wrap around to the other side of the screen
}
}

It's pretty simple, the sprite will remain at the walking sprite because that's the last sprite you've given a command to changes into.
You also need to make a command to show the standing sprite for when it stops walking.
Place this at the end of your if-else-statement:
else
{
sprite_index = spr_denpman_Loop; //stops the player if there's no key pressed.
}
(And replace the spr_denpman_Loop with the name of your standing sprite)
And on a sidenote, please look at your own code and understand what you're reading as well. I know the demo you're using, and that only explains the very basics of the program, I can't recommend using that as a base for a game. Since a lot of code can be simplified.

Related

Transforming a vector to adhere to a complimentary vector with ThreeJS

I'm facing a troublesome problem while trying to create a game engine in threeJS.
It is a math problem, but also a programming problem.
I've implemented a velocity based movement system for the player's avatar - I've used a tank in this example.
Currently, when the player hits a wall, regardless of the angle, the tank invariably stops dead.
However, I want it to be the case that the tank's velocity changes, having been coerced to follow the angle of the wall, and also reduced by a magnitude that is related to that angle.
For example, in FIG A, upon hitting the wall, the Tank continues to try and move forwards, but it's velocity is altered so that it now moves forwards, and sideways, at a reduced rate.
In FIG B, the tank hits the wall dead-on, and its overall velocity reaches 0.
In FIG C, the tank glances off the wall, and its overall velocity is only reduced by a small amount.
I've realised that I need to somehow combine the Tank's velocity vector with the wall's normal vector, to produce the adjusted vector, but I am struggling with how to represent this mathematically / programmatically.
I've tried using: tank.velocity.multiply(wallFaceNormal); (both tank.velocity and wallFaceNormal are Vector3 objects.) but this only seems to work as intended when the wall is either at angles of 0, 90, 180 or 270.
since a tank will not jump or fly, you should be fine with just a 2D-System for your calculation?
i found a link describing the physics of car hitting a solid brick wall.
http://colgatephys111.blogspot.com/2017/12/guardrail-lessens-force-of-impact.html
hope thats gonna help you a bit!
edit:
so, out of curiosity, i asked an theoretical physicist over the phone about your issue.
you got 2 seperate problems to solve:
1. P1 what is the velocity v' while hitting the wall?
2. P2 what is the new angle of the vehicel?
P2 should be fairly easy, considering your tank is adapting the angle of the wall you only need to calculate in which direction the wall is "pointing".
P1 in physics, we would talk about the reduced force and not the velocity, but given a constant limit to the force F1 (eg. your engine) resulting in a constant maxspeed,
and with a given force the wall has on the vehicel F2
v = F1
v' = F1'
F1' = F1 - F2
i think
https://www.thoughtco.com/what-is-the-physics-of-a-car-collision-2698920
explains what to do
Some code provided by a Physicist, which partly worked when I converted it to Javascript and applied it to the program:
Vector3 wallNormal = new Vector3(-0.5, 0.0, 0.5);
Vector3 incomingVelocity = new Vector3(0.0, 0.0, -1.0);
double magnitudeProduct = wallNormal.Length() * incomingVelocity.Length();
double angleBetweenVelocityAndWall = ((-incomingVelocity).Dot(wallNormal)) / (magnitudeProduct);
double newVelocityMagnitude = incomingVelocity.Length() * Math.Sin(angleBetweenVelocityAndWall);
Vector3 upVector =incomingVelocity.Cross(wallNormal);
Vector3 newDirection = wallNormal.Cross(upVector);
Vector3 newVelocity = newDirection.Normalise() * newVelocityMagnitude;
I've done some work on this problem and produced a mini game "framework" that includes an environment collision and movement attenuation utility.
I've written an article that explains how it works, which can be found here. http://www.socket-two.com/main/resource/hdoc-tutorial
But for the sake of the integrity of the thread, here's an adaptation of the portion that describes one of the approaches that can be used to attenuate motion in a ThreeJS simulation:
...
Crucially, my interest has not been to create games that involve large amount of physics, but just to create games where:
A player cannot walk through walls
A player cannot fall through floors
I've made a handful of attempts at implementing a system that would achieve this behaviour, but none of them have really worked satisfactorily. Until now.
In terms of how the ECS fits into the app architecture, it is a utility class. This is its API shape:
class Planeclamp {
constructor({ floors /*Mesh[]*/, walls /*Mesh[]*/ })
getSafePosition(startingPositionIn /*Vector3*/, intendedPositionIn /*Vector3*/) // Returns safePosition, which is a Vector3
}
As you can see, its a class that accepts two arrays of meshes in its constructor: Meshes that should be treated as floors, and meshes that should be treated as walls. Now of course in reality, there is no clear distinction between a steep floor and a shallow-angled wall, but for the purposes of the simulation, the distinction has a very reasonable integrity, and will simplify the environment collision system logic greatly.
Once you've constructed an instance of the Planeclamp class, you can then invoke it's getSafePosition method, to transform a starting position and an intended position into an attenuated position. Being the discerning reader that you are, you will have deduced that the attenuated position is the intended position, having been changed a bit if any collisions have been detected by the utility.
This is how it can be used in the game loop, to ensure a player does not pass through walls or floors:
const planeclamp = new Planeclamp({
floors: [someFloorMesh, someOtherMesh],
walls: [houseMesh, perimeterMesh, truckMesh],
});
const player = new Player();
console.log(player.cage); // Object3D
let playerPreviousPosition = player.cage.position; // Vector3
function gameLoop(delta) {
const playerIntendedPosition = new Three.Vector3(
playerPreviousPosition.x,
playerPreviousPosition.y + (10 * delta), // i.e. Gravity
playerPreviousPosition.z + (1 * delta), // i.e. Walking forwards
);
let {
safePosition, // Vector3
grounded, // Boolean
groundMaterial, // String
} = planeclamp.getSafePosition(playerPreviousPosition, playerIntendedPosition);
player.cage.position.copy(safePosition);
playerPreviousPosition = player.cage.position; // Vector3
}
And thats about it! If you would like to use this utility, you can find it in the repository. But if you would like to know more about the logic behind its workings, read on.
The Planeclamp.getSafePosition method works out a safe position in two stages. Firstly, it uses a vertical raycaster to take a look at what is underneath the player, to then see if it should stop the player from moving downwards any further. Secondly, it uses horizontal raycasters to see if it should stop the player from moving horizontally. Lets look at the vertical constraint procedure first - this is the more simple of the two steps.
// Before we do anything, create a variable called "gated".
// This will contain the safe new position that we will return at the end of
// the function. When creating it, we let it default to the
// intended position. If collisions are detected throughout the lifecycle
// of this function, these values will be overwritten.
let gated = {
x: intendedPosition.x,
y: intendedPosition.y,
z: intendedPosition.z,
};
// Define the point in 3D space where we will shoot a ray from.
// For those who haven't used raycasters before, a ray is just a line with a direction.
// We use the player's intended position as the origin of the ray, but we
// augment this by moving the origin up a little bit (backStepVert) to prevent tunneling.
const start = intendedPosition.clone().sub(new Three.Vector3(
0,
(backStepVert * -1) - (heightOffset / 2),
0)
);
// Now, define the direction of the ray, in the form of a vector.
// By giving the vector X and Z values of 0, and a Y value of -1,
// the ray shoots directly downwards.
const direction = new Three.Vector3(0, -1, 0).normalize();
// We now set the origin and direction of a raycaster that we instantiated
// in the class constructor method.
this.raycasters.vert.set(start, direction);
// Now, we use the `intersectObjects` method of the ray.
// This will return to us an array, filled with information about each
// thing that the ray collided with.
const dirCollisions = this.raycasters.vert.intersectObjects(this.floors, false);
// Initialise a distanceToGround, a grounded variable, and a groundMaterial variable.
let distanceToGround = null;
let grounded = false;
let groundMaterial = null;
// If the dirCollisions array has at least one item in it, the
// ray passed through one of our floor meshes.
if (dirCollisions.length) {
// ThreeJS returns the nearest intersection first in the collision
// results array. As we are only interested in the nearest collision,
// we pluck it out, and ignore the rest.
const collision = dirCollisions[0];
// Now, we work out the distance between where the players feet
// would be if the players intended position became the players
// actual position, and the collided object.
distanceToGround = collision.distance - backStepVert - heightOffset;
// If the distance is less than 0, then the player will pass through
// the groud if their intended position is allowed to become
// their actual position.
if (distanceToGround < 0) {
// We dont want that to hapen, so lets set the safe gated.y coordinate
// to the y coordinate of the point in space at which the collision
// happened. In other words, exactly where the ground is.
gated.y = intendedPosition.y - distanceToGround;
// Make a note that the player is now grounded.
// We return this at the end of the function, along with
// the safe position.
grounded = true;
// If the collided object also has a groundMaterial set inside
// its userData (the place that threeJS lets us attach arbitrary
// info to our objects), also set the groundMaterial. This is
// also returned at the end of the function alongside the grounded
// variable.
if (collision.object.userData.groundMaterial) {
groundMaterial = collision.object.userData.groundMaterial;
}
}
}
And thats it for vertical environment constraints. Simples!
The horizontal environment constraint system is a bit more complex. But in its essence, what it does is:
Work out the horizontal direction the player is travelling in. In olde worlde terms, this can be thought of as North, South, SouthEast, SouthSouthWest etc, but in ThreeJS it is represented by a Vector.
Cast a ray in the direction that the player is travelling in.
Use the ray to find out if allowing the players intended position would cause the player to pass through any of the wall meshes.
And it is at this point that the horizontal ECS becomes more complex than the vertical ECS. With the vertical ECS, if a collision happens, we can just set the players Y position to the Y position of the point at which the collision happened - effectively halting the players Y movement. However, it we did this for horizontal movement, it would make for a very frustrating game experience.
If the player was running head on into a wall, and was stopped dead in their tracks, this would be fine. But if the player moved into the wall at a very shallow angle, and merely grazed it, it would appear that they had "gotten stuck" on the wall, and would find themselves having to reverse away from it, and take care not to touch it again.
What we actually want to happen, is have the player's horizontal velocity attenuated, so that they move along the wall. Therefore, the horizontal ECS proceeds as follows:
Obtain the normal of the surface that was collided with. (For our purposes, a normal can be described as the direction that the wall is facing)
Inspect the difference between the wall normal direction, and the player's movement direction.
Use the difference to work out a safe position, which is the point in space that the collision happened, incremented by a vector that is horizontally perpendicular to the wall normal, multiplied by the cross product of the players input direction and the wall normal.
...
Here is the final utility class, in full:
import * as Three from '../../../vendor/three/three.module.js';
class Planeclamp {
constructor({
scene,
floors = [],
walls = [],
drawRays = true,
} = {}) {
this.drawRays = drawRays;
this.floors = [];
this.walls = [];
this.scene = scene;
this.objects = [];
// Init collidable mesh lists
this.addFloors(floors);
this.addWalls(walls);
// Create rays
this.raycasters = {
vert: new Three.Raycaster(),
horzLeft: new Three.Raycaster(),
horzRight: new Three.Raycaster(),
correction: new Three.Raycaster(),
};
}
setDrawRays(draw) {
this.drawRays = draw;
}
addFloor(floor) {
this.floors.push(floor);
}
removeFloor(floor) {
this.floors = this.floors.filter(thisFloor => thisFloor !== floor);
}
addFloors(floors) {
floors.forEach(floor => this.addFloor(floor));
}
resetFloors() {
this.floors = [];
}
addWall(wall) {
this.walls.push(wall);
}
removeWall(wall) {
this.walls = this.walls.filter(thisWall => thisWall !== wall);
}
addWalls(walls) {
walls.forEach(wall => this.addWall(wall));
}
resetWalls() {
this.walls = [];
}
getSafePosition(startingPositionIn, intendedPositionIn, {
collisionPadding = .5,
heightOffset = 0,
} = {}) {
// ------------------ Setup -------------------
// Parse args
const startingPosition = startingPositionIn.clone();
const intendedPosition = intendedPositionIn.clone();
let grounded = false;
let groundMaterial = null;
// Augmenters
const backStepVert = 50;
const backStepHorz = 5;
const backStepCorrection = 5;
// Prepare output
let gated = {
x: intendedPosition.x,
y: intendedPosition.y,
z: intendedPosition.z,
};
// Clean up previous debug visuals
this.objects.map(object => this.scene.remove(object));
this.objects = [];
// ------------------ Vertical position gating -------------------
// Adjust vertical position in gated.y.
// Store grounded status in grounded.
const start = intendedPosition.clone().sub(new Three.Vector3(
0,
(backStepVert * -1) - (heightOffset / 2),
0)
);
const direction = new Three.Vector3(0, -1, 0).normalize();
this.raycasters.vert.set(start, direction);
const dirCollisions = this.raycasters.vert.intersectObjects(this.floors, false);
if (this.drawRays) {
const arrowColour = dirCollisions.length ? 0xff0000 : 0x0000ff;
const arrow = new Three.ArrowHelper(this.raycasters.vert.ray.direction, this.raycasters.vert.ray.origin, 300, arrowColour);
this.objects.push(arrow);
}
let distanceToGround = null;
if (dirCollisions.length) {
const collision = dirCollisions[0];
distanceToGround = collision.distance - backStepVert - heightOffset;
if (distanceToGround < 0) {
gated.y = intendedPosition.y - distanceToGround;
grounded = true;
if (collision.object.userData.groundMaterial) {
groundMaterial = collision.object.userData.groundMaterial;
}
}
}
// ------------------ Horizontal position gating -------------------
const horizontalOutputPosition = (() => {
// Init output position
const outputPosition = new Three.Vector3(intendedPosition.x, 0, intendedPosition.z);
// Store normalised input vector
const startingPos = startingPosition.clone();
const intendedPos = intendedPosition.clone();
startingPos.y = startingPositionIn.y + .5;
intendedPos.y = startingPositionIn.y + .5;
let inputVector = intendedPos.clone().sub(startingPos).normalize();
// Work out distances
const startingIntendedDist = startingPos.distanceTo(intendedPos);
const inputSpeed = startingIntendedDist;
// Define function for moving ray left and right
function adj(position, offset) {
const rayAdjuster = inputVector
.clone()
.applyAxisAngle(new Three.Vector3(0, 1, 0), Math.PI / 2)
.multiplyScalar(.5)
.multiplyScalar(offset);
return position.clone().add(rayAdjuster);
}
// Work out intersections and collision
let collisions = {
left: {
collision: null
},
right: {
collision: null
}
};
Object.keys(collisions).forEach(side => {
const rayOffset = side === 'left' ? -1 : 1;
const rayStart = adj(startingPos.clone().sub(inputVector.clone().multiplyScalar(2)), rayOffset);
const startingPosSide = adj(startingPos, rayOffset);
const intendedPosSide = adj(intendedPos, rayOffset);
const startingIntendedDistSide = startingPosSide.distanceTo(intendedPosSide);
const rayKey = 'horz' + _.startCase(side);
this.raycasters[rayKey].set(rayStart, inputVector);
const intersections = this.raycasters[rayKey].intersectObjects(this.walls, true);
for (let i = 0; i < intersections.length; i++) {
if (collisions[side].collision) break;
const thisIntersection = intersections[i];
const startingCollisionDist = startingPosSide.distanceTo(thisIntersection.point);
if (startingCollisionDist - collisionPadding <= startingIntendedDistSide) {
collisions[side].collision = thisIntersection;
collisions[side].offset = rayOffset;
}
}
if (inputSpeed && this.drawRays) {
this.objects.push(new Three.ArrowHelper(this.raycasters[rayKey].ray.direction, this.raycasters[rayKey].ray.origin, 300, 0x0000ff));
}
});
const [ leftCollision, rightCollision ] = [ collisions.left.collision, collisions.right.collision ];
const collisionData = (leftCollision?.distance || Infinity) < (rightCollision?.distance || Infinity) ? collisions.left : collisions.right;
if (collisionData.collision) {
// Var shorthands
const collision = collisionData.collision;
const normalVector = collision.face.normal.clone();
normalVector.transformDirection(collision.object.matrixWorld);
normalVector.normalize();
// Give output a baseline position that is the same as the collision position
let paddedCollision = collision.point.clone().sub(inputVector.clone().multiplyScalar(collisionPadding));
paddedCollision = adj(paddedCollision, collisionData.offset * -1);
outputPosition.x = paddedCollision.x;
outputPosition.z = paddedCollision.z;
if (leftCollision && rightCollision && leftCollision.face !== rightCollision.face) {
return startingPos;
}
// Work out difference between input vector and output / normal vector
const iCAngleCross = inputVector.clone().cross(normalVector).y; // -1 to 1
// Work out output vector
const outputVector = (() => {
const ivn = inputVector.clone().add(normalVector);
const xMultiplier = ivn.x > 0 ? 1 : -1;
const zMultiplier = ivn.z > 0 ? 1 : -1;
return new Three.Vector3(
Math.abs(normalVector.z) * xMultiplier,
0,
Math.abs(normalVector.x) * zMultiplier,
).normalize();
})();
if (inputSpeed && this.drawRays) {
this.objects.push(new Three.ArrowHelper(normalVector, startingPos, 300, 0xff0000));
}
// Work out output speed
const outputSpeed = inputSpeed * Math.abs(iCAngleCross) * 0.8;
// Increment output position with output vector X output speed
outputPosition.add(outputVector.clone().multiplyScalar(outputSpeed));
}
// ------------------ Done -------------------
return outputPosition;
})();
gated.x = horizontalOutputPosition.x;
gated.z = horizontalOutputPosition.z;
// ------------------ Culmination -------------------
// Add debug visuals
this.objects.map(object => this.scene.add(object));
// Return gated position
const safePosition = new Three.Vector3(gated.x, gated.y, gated.z);
return { safePosition, grounded, groundMaterial };
}
}
export default Planeclamp;

Game Maker: create an ennemy at a random position on the ground

I know a bit about coding (in Python, C and XHTML) and I'm trying to understand the basics of Game Maker. I have created a room with enemies moving, colliding to the walls and all, but now, I'd like to randomly spawn enemies in the room as long as they are on a ground. For now, it only works when I spawn them randomly.
Here is the code I put in the Create event of the obj_enemy but obviously something isn't working since it does not spawn any enemy at all.
Also, don't know if it matters, but if I haven't already placed myself an obj_enemy in the room, they do not spawn neither...
// INIT //
dir = -1; // direction
movespeed = 3; // movement speed
hsp = 0; // horizontal speed
vsp = 0; // vertical speed
grav = 0.5; // gravity
// CREATE //
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// If there is less than 4 ennemies
if instance_number (obj_ennemy) <= 4
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}
This is the create event of obj_enemy. if there are no obj_enemy's in the room then this code will never get run!
You either need to start with at least one enemy in the room or create a controller object in charge of creating enemies that you put into the room instead (I recommend this approach).
Also even if the code does get run then the chances of spawning an enemy just over a wall in the correct location is quite small so you will have to run the program many times before you see it happen. To avoid this just put the spawn code into a while true loop and break from it once 4 enemies have been spawned:
while (instance_number (obj_ennemy) <= 4)
{
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}

How to make an enemy stop firing bullets at player when there is a wall between them

I have three object, obj_player, obj_enemy, obj_wall. Now, I have this code from gmc forums.
shoot_cooldown -= 1; //Lower the shooting cooldown timer
if (shoot_cooldown < 0) then shoot_cooldown = 0 //Prevents timer from counting down further than 0.
target_distance = distance_to_object(obj_player); //Distance to the player from enemy.
if (target_distance < 64) //If player within the range of enemy
{
//image_angle = point_direction(x,y,obj_player.x,obj_player.y); //Enemy faces the player.
if (shoot_cooldown == 0) //If enemy can shoot (cooldown ready)
{
bullet = instance_create(x,y,obj_bullet); //Create a bullet relative to enemy
bullet.direction = point_direction(x,y,obj_player.x,obj_player.y); //Shoot it towards player
bullet.speed = 3; //Give it speed
shoot_cooldown = 50; //Set the new cooldown time between low and high thresholds.
}
}
This works perfectly. However, the obj_player and obj_enemy have the dimensions of 64x64. I have in my code that if the player's distance between the enemy is equal or below 64pix, enemy will fire a bullet. Now the obj_wall has the dimension of 32x32. If the player is on the other side of the wall, the enemy must not fire a bullet since enemy "can't detect" the player. But the enemy still fires a bullet because the player is within 64 pix. I wonder if there is a workaround in making the enemy stop firing if there is a wall between them. Thanks for those who will reply. I know there is gmc forums I'm hoping someone can also help me here.
Use collision_line between obj_enemy and obj_player, like
if target_distance < 64 and collision_line(x, y, obj_player.x, obj_player.y, obj_wall, false, true) == noone
{
// shoot
}
for more perfect you can check line between enemy and every corner of the player

2D Continuous Collision Detection

I'm trying to implement simple continuous collision detection for my pong game however i'm not sure i'm implementing or understand this right. AFAIR continuous collision detection is used for fast moving objects that may pass through another object circumventing normal collision detection.
So what I tried was that because the only fast moving object I have is a ball I would just need the position of the ball, its move speed, and the position of the object we are comparing to.
From this I figured it would be best that for example if the ball's move speed indicated it was moving left, I would compare it's left-most bound to the right-most bound of the other object. From this I would step through by adding the move speed to the left-most bound of the ball and compare to make sure it's greater than the other objects right bound. This would show that there is no left right collision.
I have something somewhat working, but unfortunately, the ball starts bouncing normally for a while then it acts as if it hits a paddle when nothing is there.
I'm a bit lost, any help would be appreciated!
static bool CheckContinuousCollision(PActor ball, PRect ballRect, PActor other, PRect otherRect)
{
PVector ballMoveSpeed;
int ballXLimit;
int ballYLimit;
ballMoveSpeed = ball.moveSpeed;
// We are moving left
if ( sgn(ball.moveSpeed.x) < 0 )
{
ballXLimit = std.math.abs(ballMoveSpeed.x) / 2;
for ( int i = 0; i <= ballXLimit; i++ )
{
if ( ballRect.Left < otherRect.Right && otherRect.Left < ballRect.Left)
{
return true;
}
ballRect.Left -= i;
}
}
//We are moving right
if ( sgn(ball.moveSpeed.x) > 0)
{
ballXLimit = std.math.abs(ballMoveSpeed.x) / 2;
for ( int i = 0; i < ballXLimit; i ++ )
{
if ( ballRect.Right > otherRect.Left && ballRect.Right < otherRect.Right )
{
return true;
}
ballRect.Right += i;
}
}
// we are not moving
if ( sgn(ball.moveSpeed.x) == 0)
{
return false;
}
}
You seem to be checking the collision of only one dimension, i.e the X dimension of your ball versus your Other.
What you probably want is to compare whether the two objects collide in 2d space. This can be easily done by adjusting each objects Bounding Rectangle and checking whether the rectangles overlap. Then in your for loop you can adjust your Ball rectangle accordingly

Collision Detection and maintaining momentum on an object

I have been implementing various forms of simple collision detection with varying results. I have a fairly good working version of collision detection, but there are some odd behaviors that I can't work out.
Just for a reference, i'm making a simple pong game, and trying to refine the collision. The problems I get are when the ball collides with the paddle on either the top or bottom side. In those cases, the ball hovers above (or below) the paddle and does not move. I'm guessing this is because of how i'm checking for collision and how i'm altering the movespeed of the ball.
I would like to implement a way I differentiate between top/bottom and left/right collision but this is the only method that works decently:
static void CheckCollision(PActor object1, PActor object2, PInput pinput)
{
if ( CheckObjectCollision( object1, object2 ) )
{
AdjustMoveSpeed( object1, object2, pinput );
}
}
static bool CheckObjectCollision(PActor object1, PActor object2)
{
int object1LeftBound = object1.position.x;
int object1RightBound = object1.position.x + object1.actorTextureXSize;
int object1TopBound = object1.position.y;
int object1BottomBound = object1.position.y + object1.actorTextureYSize;
int object2LeftBound = object2.position.x;
int object2RightBound = object2.position.x + object1.actorTextureXSize;
int object2TopBound = object2.position.y;
int object2BottomBound = object2.position.y + object2.actorTextureYSize;
if ( object1RightBound < object2LeftBound )
return false;
if ( object1LeftBound > object2RightBound )
return false;
if ( object1BottomBound < object2TopBound )
return false;
if ( object1TopBound > object2BottomBound )
return false;
return true;
}
I am guessing that the root of some of the problems i'm having is the function AdjustMoveSpeed, here it is:
static void AdjustMoveSpeed(PActor object1, PActor object2, PInput pinput)
{
PVector prevMouseLocation = pinput.GetPrevMouseLocation();
PVector currMouseLocation = pinput.GetCurrMouseLocation();
int currentMoveSpeed;
int nextMoveSpeed;
if (typeid(object1) == typeid(PBall))
{
object1.moveSpeed.x *= -1;
if ( typeid(object2) == typeid(PPlayer) )
{
currentMoveSpeed = object1.moveSpeed.y;
nextMoveSpeed = prevMouseLocation.y - currMouseLocation.y;
object1.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
}
else
{
if (object1.moveSpeed.y > 0)
object1.moveSpeed.y *= -1;
}
}
else if (typeid(object2) == typeid(PBall))
{
object2.moveSpeed.x *= -1;
if ( typeid(object1) == typeid(PPlayer) )
{
currentMoveSpeed = object1.moveSpeed.y;
nextMoveSpeed = prevMouseLocation.y - currMouseLocation.y;
object2.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
}
else
{
if (object2.moveSpeed.y > 0)
object2.moveSpeed.y *= -1;
}
}
}
What I was attempting to do with AdjustMoveSpeed, is first check to see which object is the ball, after this, multiply the x move speed by -1 to reverse its direction. After this, I check to see if the other object is a player, if so I set the y move speed to the difference between the previous mouse location and current mouse location. This is here to give the player option to change the balls y speed, or add spin.
I've tried checking for intersection between objects so that I can get a specific side, and the result is the ball just flying in the middle of the screen without actually hitting either paddle.
How do I properly check for collision detection on two objects that are squares?
How can I fix AdjustMoveSpeed so that it works properly with collision detection?
Lastly, how do I keep the momentum of the ball of its current speed is greater than the difference of the mouse location before and after the hit?
I've tried taking comparing the absolute value of currentMoveSpeed and nextMoveSpeed but then the ball doesn't change y speed. Something like this:
if ( abs(currentMoveSpeed) < abs(nextMoveSpeed )
object1.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
else
object1.moveSpeed.y *= -1
Pong is simple enough that, rather than moving the ball each frame and checking for a collision with a paddle, you can actually solve the equation for when the paddle and ball will collide - if that time is less than one frame, there is a collision.
This completely eliminates the issue of the ball moving so fast it moves through the paddle, an issue that plagues many pong-clones that use the naive method of collision-detection.
This solution is called continuous collision detection - see this answer for more information.
If the ball gets stuck on the paddle instead of bouncing it is probably because it keeps changing direction back and forth. The ball should only bounce if it is heading towards the paddle.
if (sgn(object1.moveSpeed.x) == sgn(object1.x - object2.x)) {
// Ball is already moving away from the paddle, don't bounce!
}
else {
// Ok to bounce!
object1.moveSpeed.x *= -1;
}

Resources