Best way to do object collision? - gml

I'm trying to do wall collision for objects and I've followed a tutorial that offers one method of doing collision.
This is the tutorial: https://www.youtube.com/watch?v=yZU1QJJdxgs
Currently, if the object detects a wall, instead of moving it's full distance, it moves pixel by pixel until it's against the wall. This worked well until I started trying to rotate the object with image_rotate, because it caused objects to get stuck in walls by either sliding against them or if they rotated into them.
I fixed this by using draw_sprite_ext instead and changing the rotation of the sprite itself and not the mask, which worked for about 20 minutes until it started causing more problems.
///obj_player Step
//Initialise Variables
hor_speed = 0;
ver_speed = 0;
accelerationspeed = 0.2;
decelerationspeed = 0.2;
maxspeed = 3;
pointdirection = 0;
//Get player's input
key_right = keyboard_check(ord("D"))
key_left = -keyboard_check(ord("A"))
key_up = -keyboard_check(ord("W"))
key_down = keyboard_check(ord("S"))
pointdirection = point_direction(x,y,mouse_x,mouse_y) + 270
hor_movement = key_left + key_right;
ver_movement = key_up + key_down;
//horizontal acceleration
if !(abs(hor_speed) >= maxspeed) {
hor_speed += hor_movement * accelerationspeed;
}
//horizontal deceleration
if (hor_movement = 0) {
if !(hor_speed = 0) {
hor_speed -= (sign(hor_speed) * decelerationspeed)
}
}
//vertical acceleration
if !(abs(ver_speed) >= maxspeed) {
ver_speed += ver_movement * accelerationspeed;
}
//vertical deceleration
if (ver_movement = 0) {
if !(ver_speed = 0) {
ver_speed -= (sign(ver_speed) * decelerationspeed)
}
}
//horizontal collision
if (place_meeting(x+hor_speed,y,obj_wall)) {
while(!place_meeting(x+sign(hor_speed),y,obj_wall)) {
x += sign(hor_speed);
}
hor_speed = 0;
}
//vertical collision
if (place_meeting(x,y+ver_speed,obj_wall)) {
while(!place_meeting(x,y+sign(ver_speed),obj_wall)) {
y += sign(ver_speed);
}
ver_speed = 0;
}
//move the player
x += hor_speed;
y += ver_speed;
///obj_player Draw
//rotate to look at cursor
draw_sprite_ext(spr_player, 0, x,y,image_xscale,image_yscale, pointdirection, image_blend, image_alpha);
I think the best way to rotate objects is through image_rotate, and I'd like to do it without getting stuff stuck in walls. Can my current method of collision be adapted to do this, or should I attempt to do it in a different way?

Your code looks fine, but if you're going to be rotating objects then you would also need to consider having a "knock back mechanic." Reason being is the player could be sitting next to this wall and if you rotate the object over them so they cant move, its not a fun time being stuck.
So you 'could' have the object that's rotating do a check before rotating and if objects are in the way then either stop it or push them back so they cant be within range.

Related

Sprite Smooth movement and facing position according to movement

i'm trying to make this interaction with keyboard for movement using some sprites and i got stuck with two situations.
1) The character movement is not going acording to the animation itself (it only begin moving after one second or so while it's already being animated). What i really want it to do is, to move without a "initial acceleration feeling" that i get because of this problem
2) I can't think of a way to make the character face the position it should be facing when the key is released. I'll post the code here, but since it need images to work correctly and is not so small i made a skecth available at this link if you want to check it out: https://www.openprocessing.org/sketch/439572
PImage[] reverseRun = new PImage [16];
PImage[] zeroArray = new PImage [16];
void setup(){
size(800,600);
//Right Facing
for(int i = 0; i < zeroArray.length; i++){
zeroArray[i] = loadImage (i + ".png");
zeroArray[i].resize(155,155);
}
//Left Facing
for( int z = 0; z < reverseRun.length; z++){
reverseRun[z] = loadImage ( "mirror" + z + ".png");
reverseRun[z].resize(155,155);
}
}
void draw(){
frameRate(15);
background(255);
imageMode(CENTER);
if(x > width+10){
x = 0;
} else if (x < - 10){
x = width;}
if (i >= zeroArray.length){
i = 3;} //looping to generate constant motiion
if ( z >= reverseRun.length){
z = 3;} //looping to generate constant motiion
if (isRight) {
image(zeroArray[i], x, 300);
i++;
} //going through the images at the array
else if (isLeft) {
image(reverseRun[z],x,300);
z++;
} going through the images at the array
else if(!isRight){
image(zeroArray[i], x, 300);
i = 0; } //"stoped" sprite
}
}
//movement
float x = 300;
float y = 300;
float i = 0;
float z = 0;
float speed = 25;
boolean isLeft, isRight, isUp, isDown;
void keyPressed() {
setMove(keyCode, true);
if (isLeft ){
x -= speed;
}
if(isRight){
x += speed;
}
}
void keyReleased() {
setMove(keyCode, false);
}
boolean setMove(int k, boolean b) {
switch (k) {
case UP:
return isUp = b;
case DOWN:
return isDown = b;
case LEFT:
return isLeft = b;
case RIGHT:
return isRight = b;
default:
return b; }
}
The movement problem is caused by your operating system setting a delay between key presses. Try this out by going to a text editor and holding down a key. You'll notice that a character shows up immediately, followed by a delay, followed by the character repeating until you release the key.
That delay is also happening between calls to the keyPressed() function. And since you're moving the character (by modifying the x variable) inside the keyPressed() function, you're seeing a delay in the movement.
The solution to this problem is to check which key is pressed instead of relying solely on the keyPressed() function. You could use the keyCode variable inside the draw() function, or you could keep track of which key is pressed using a set of boolean variables.
Note that you're actually already doing that with the isLeft and isRight variables. But you're only checking them in the keyPressed() function, which defeats the purpose of them because of the problem I outlined above.
In other words, move this block from the keyPressed() function so it's inside the draw() function instead:
if (isLeft ){
x -= speed;
}
if(isRight){
x += speed;
}
As for knowing which way to face when the character is not moving, you could do that using another boolean value that keeps track of which direction you're facing.
Side note: you should really try to properly indent your code, as right now it's pretty hard to read.
Shameless self-promotion: I wrote a tutorial on user input in Processing available here.

How do I randomize the starting direction of a ball in Spritekit?

I've started trying a few things with SpriteKit for Game Development. I was creating a brick breaking game. So I've run into a issue on how to randomize the starting direction of the ball.
My ball has the following properties
ball.physicsBody.friction = 0;
ball.physicsBody.linearDamping = 0;
ball.physicsBody.restitution = 1 ; //energy lost on impact or bounciness
To start at different direction during the gameplay, I've randomized the selection of the 4 vectors because I'm using the applyImpulse method to direct the ball in a particular direction and I need to make sure the ball does not go slow if the vector values are low.
int initialDirection = arc4random()%10;
CGVector myVector;
if(initialDirection < 2)
{
myVector = CGVectorMake(4, 7);
}
else if(initialDirection >3 && initialDirection <= 6)
{
myVector = CGVectorMake(-7, -5);
}
else if(initialDirection >6 && initialDirection <= 8)
{
myVector = CGVectorMake(-5, -8);
}
else
{
myVector = CGVectorMake(8, 5);
}
//apply the vector
[ball.physicsBody applyImpulse:myVector];
Is this the right way to do it? I tried using applyForce method but then, ball slowed down after the force was applied.
Is there any way I can randomize the direction and still maintain a speed for my ball ?
The basic steps
Randomly select an angle in [0, 2*PI)
Select the magnitude of the impulse
Form vector by converting magnitude/angle to vector components
Here's an example of how to do that
ObjC:
CGFloat angle = arc4random_uniform(1000)/1000.0 * M_PI_2;
CGFloat magnitude = 4;
CGVector vector = CGVectorMake(magnitude*cos(angle), magnitude*sin(angle));
[ball.physicsBody applyImpulse:vector];
Swift
let angle:CGFloat = CGFloat(arc4random_uniform(1000)/1000) * (CGFloat.pi/2)
let magnitude:CGFloat = 4
let vector = CGVector(x:magnitude * cos(angle), y:magnitude * sin(angle))
ball.physicsBody?.applyImpulse(vector)

Looping moving background objective-C

I am testing a background-loop animation where there will be to images both 1024x768 pixels in dimension, move leftwards, go offscreen, then jump back to the other side, and repeat.
I was able to do this by creating a constant speed for both background image to move (successful), and then I tried the following code to make it jump, but there was a problem:
if((background.center.x) < -511){
background.center = CGPointMake(1536, background.center.y);
}
if((background2.center.x) < -511){
background2.center = CGPointMake(1536, background2.center.y);
}
Somehow this is not working the way I expected. It leaves a few pixels of gap every time, and I am confused why. Does anyone know what's causing this to happen and how to fix it? Thanks!
It seems like you have forgotten to take into account the distance moved. The greater than expression might have been triggered because you moved to far. I guess your movement is larger than 1 pixel/frame.
I am not sure what kind of values that are feeding your movement but I think to take into account the movement you should do something like...
if ((background.center.x) < -511){
CGFloat dist = background.center.x + 512;
background.center = CGPointMake(1536+dist, background.center.y);
}
if ((background2.center.x) < -511){
CGFloat dist = background2.center.x + 512;
background2.center = CGPointMake(1536+dist, background2.center.y);
}
Rather than have the two images move (sort of) independently, I would keep track of a single backgroundPosition variable and then constantly update the position of both images relative to that one position. This should keep everything nice and tidy:
CGFloat const backgroundWidth = 1024;
CGFloat const backgroundSpeed = 2;
- (void)animateBackground {
backgroundPosition -= backgroundSpeed;
if (backgroundPosition < 0) {
backgroundPosition += backgroundWidth;
}
background1.center.x = backgroundPosition - backgroundWidth/2;
background2.center.x = backgroundPosition + backgroundWidth/2;
}

Dynamic grid using FlashDevelop & Actionscript 2.0

I'm new to actionscript. What I'm tryin to do is simulate traffic flow near a 2 lane intersection, following Wolfram's rule 184. To begin with, I'm trying to create a grid (8x8 of which the intersection is between the middle two rows and the middle two columns, like a plus sign) whose cells have the following attributes:
color = white;
car = false;
when clicked:
color = red;
car = true (a car is present);
So, after the user clicks cells to position the cars initially and presses the start button, the simulation will begin.
Here's my code so far (apologies for incorrect formatting):
class Main
{
private var parent:MovieClip;
public static function main(mc:MovieClip)
{
var app = new Main(mc);
}
public function Main(mc:MovieClip)
{
this.parent = mc;
//grid settings
var Cell:MovieClip = mc.createEmptyMovieClip("cell", mc.getNextHighestDepth());
var x:Number = 0;
var y:Number = 0;
var color:Number = 0xffffff;
var car:Boolean = false;
for (y = 0; y < 3 * Stage.height / 8; y += Stage.height / 8)
{
for (x = 3*Stage.width/8; x < 5*Stage.width/8; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8) - 5, (Stage.height / 8) - 5, color, 100);
}
}
for (y = 3*Stage.height/8; y < 5 * Stage.height / 8; y += Stage.height / 8)
{
for (x = 0; x < Stage.width; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8)-5, (Stage.height / 8)-5, color, 100);
}
}
for (y = 5*Stage.height/8; y < Stage.height; y += Stage.height / 8)
{
for (x = 3*Stage.width/8; x < 5*Stage.width/8; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8)-5, (Stage.height / 8)-5, color, 100);
}
}
Cell.onMouseDown()
{
Cell.color = UI.RED;
Cell.car = true;
}
}
}
I know there's quite a few things gone wrong here. First of all, the cell color doesn't change on mouse down. Do i need to make movie clip for each cell in the for loops? I think it would be easier to make a grid of objects with given attributes, but i don't know how to do that. Would really appreciate if someone helps me out.
From what I can tell, issue with your current approach is that using drawRect() literally draws pixels on to the stage, which means you'll have no reference to those shapes in future frames. right now, you've got one MovieClip that has been drawn many times. What you need is a lot of MovieClips so you have a reference to each cell that you can update/edit every frame.
Your best bet is to do the following (I'll just provide pseudo because I'm a bit shaky on AS2 syntax):
A) Create an array to hold all of the Cells. Call it:
var Cells:Array = new Array();
B) During each step of the loops in your constructor, do 4 things.
1) Create a new MovieClip `var tempCell:MovieClip = new MovieClip();
2) Draw a rectangle on to each MovieClip: A tutorial for the graphics API in AS2 http://www.actionscript.org/resources/articles/727/1/Drawing-shapes-with-AS2/Page1.html
3) Add an event listenerto each MovieClip that points to a common event handler. This listener listens for mouse clicks on that MovieClip (or MOUSE_DOWN)
4) and use Cells.push(tempClip) to add that new MovieClip to your array so you now have one object that contains a reference to all of your cells.
C) Create an click event handler that redraws the cell that has been clicked. Try MouseEvent.target
You have another option to using the graphics API to draw rectangles, and that is to simply add and remove stock graphics from your Flash library. You'll have to draw these graphics in Flash and then 'Export for Actionscript' to call them up.
Hope this points you in the right direction!
J

Pathfinding / Deciding on direction

i'm making a simple iPhone game using cocos2d-iphone. I have an array of fiends, the "fiendSet" which has to navigate around a field full of obstacles. I spent the last three nights trying to get my A* pathfinding to work. I found the actual A* implementation here on stackoverflow and it works brilliantly. However, once i try to move my fiends around i run into trouble.
Each of my fiends has a CGPoint called motionTarget which contains the x and y values for where the fiend has to go. If only set the positions x and y to absolute values once a second, it works, like so:
-(void) updateFiendPositions:(ccTime)dt {
for (MSWFiend *currFiend in fiendSet) {
currFiend.position = ccp(currFiend.motionTarget.x,currFiend.motionTarget.y);
}
}
However, this doesn't look very nice, the fiends just "jump" 20px each second instead of animating nicely. I only implemented this as a placeholder method to verify the pathfinding. Now i want smooth animation. This is what i did:
-(void) updatePositions:(ccTime) dt {
for (MSWFiend *currFiend in fiendSet) {
if (currFiend.motionTarget.x != -1 && currFiend.motionTarget.y != -1) {
float x,y;
if ((int)floor(currFiend.position.x) < (int)floor(currFiend.motionTarget.x)) {
x = currFiend.position.x+(currFiend.speed*dt);
}
if ((int)floor(currFiend.position.x) > (int)floor(currFiend.motionTarget.x)) {
x = currFiend.position.x-(currFiend.speed*dt);
}
if (abs((int)floor(currFiend.position.x)-(int)floor(currFiend.motionTarget.x)) < 2) {
x = currFiend.motionTarget.x;
}
if ((int)floor(currFiend.position.y) < (int)floor(currFiend.motionTarget.y)) {
y = currFiend.position.y+(currFiend.speed*dt);
}
if ((int)floor(currFiend.position.y) > (int)floor(currFiend.motionTarget.y)) {
y = currFiend.position.y-(currFiend.speed*dt);
}
if (abs((int)floor(currFiend.position.y)-(int)floor(currFiend.motionTarget.y)) < 2) {
y = currFiend.motionTarget.y;
}
currFiend.position = ccp(x,y);
}
}
}
This works great for fiends moving in one direction. As soon as a fiend is supposed to go around a bend, trouble starts. Instead of for example first going up, then right, then down; my fiends will combine the up/right motion into one, they are "cutting corners". I only want my fiends to move either north/south OR east/west for each position update, not both. In other words, i don't want to animate changes to x and y simultaneously. I hope this explanation is clear enough..
I'm pretty sure i have a logic error somewhere.. i just havn't been able to figure it out for the last three sleepless nights after work.. Help!
You have to keep track of each node in the path to the target. That way you only animate the motion to the next node. Also you can use a CCMoveTo action instead on doing the animation yourself.
#Aleph thanks for your suggestion. I found that it was the code which determines when to assign a new motionTarget, that was faulty, not the code i posted to begin with. When you mentioned keeping track of each nodes position, i thought of my motionTarget determination code and found the error after 2-3 hours. I ended up doing it like this:
-(void) updatePositions:(ccTime) dt {
for (MSWFiend *currFiend in fiendSet) {
int fiendGX,fiendGY,targetGX,targetGY,dGX,dGY;
float x,y,snappedX,snappedY;
BOOL snappedIntoPosition = FALSE;
fiendGX = (int)round(100.0f*(currFiend.position.x/20));
fiendGY = (int)round(100.0f*(currFiend.position.y/20));
targetGX = (int)round(100.0f*(currFiend.motionTarget.x/20));
targetGY = (int)round(100.0f*(currFiend.motionTarget.y/20));
snappedX = currFiend.position.x;
snappedY = currFiend.position.y;
dGX = abs(fiendGX-targetGX);
dGY = abs(fiendGY-targetGY);
float snappingThreshold; //1 = snap when 0.1 from motionTarget.
snappingThreshold = currFiend.speed/10;
if (dGX < snappingThreshold && dGY < snappingThreshold) {
snappingThreshold = currFiend.motionTarget.x;
snappingThreshold = currFiend.motionTarget.y;
int newPathStep;
newPathStep = currFiend.pathStep + 1;
currFiend.pathStep = newPathStep;
}
int gX,gY;
gX = [[currFiend.path objectAtIndex:currFiend.pathStep] nodeX];
gY = (tileMap.mapSize.height-[[currFiend.path objectAtIndex:currFiend.pathStep] nodeY])-1;
currFiend.motionTarget = ccp(gX*20,gY*20); //Assign motion target to the next A* node. This is later used by the position updater.
if (currFiend.motionTarget.x != -1 && currFiend.motionTarget.y != -1) {
x = currFiend.motionTarget.x;
y = currFiend.motionTarget.y;
if ((int)floor(currFiend.position.x) < (int)floor(currFiend.motionTarget.x)) {
//Move right
x = snappedX+(currFiend.speed*dt);
if (x > currFiend.motionTarget.x) {
x = currFiend.motionTarget.x;
}
y = snappedY;
}
if ((int)floor(currFiend.position.x) > (int)floor(currFiend.motionTarget.x)) {
//Move left
x = snappedX-(currFiend.speed*dt);
if (x < currFiend.motionTarget.x) {
x = currFiend.motionTarget.x;
}
y = snappedY;
}
if ((int)floor(currFiend.position.y) < (int)floor(currFiend.motionTarget.y)) {
//Move up
y = snappedY+(currFiend.speed*dt);
if (y > currFiend.motionTarget.y) {
y = currFiend.motionTarget.y;
}
x = snappedX;
}
if ((int)floor(currFiend.position.y) > (int)floor(currFiend.motionTarget.y)) {
//Move down
y = snappedY-(currFiend.speed*dt);
if (y < currFiend.motionTarget.y) {
y = currFiend.motionTarget.y;
}
x = snappedX;
}
}
currFiend.position = ccp(x,y);
}
}