How can I make the player stop when it's collding with an object but when it's touching the x side of an object, it can still slide up and down, and when it's touching the y side of an object, it can still slide side to side while colliding.
Here is my code for player movement.
public function onKeyDown(event: KeyboardEvent): void
{
if (event.keyCode == Keyboard.D)
{
isRight = true;
}
if (event.keyCode == Keyboard.A)
{
isLeft = true;
}
if (event.keyCode == Keyboard.W)
{
isUp = true;
}
if (event.keyCode == Keyboard.S)
{
isDown = true;
}
}
public function onEnterFrame(event: Event): void
{
if (isRight)
{
x += 5;
}
if (isLeft)
{
x -= 5;
}
if (isUp)
{
y -= 5;
}
if (isDown)
{
y += 5;
}
}
On each tick of movement (OnEnterFrame in your case) you should perform the following basic steps:
Save player's x and y into variables
Calculate new position
Check if player overlaps any objects or level boundaries in his new position. This can be done in a variety of ways; it's not possible to elaborate without knowing the structure of your level, shape of player, shapes of obstacles, how you store the information about them.
For every overlapping object calculate how much the player overlaps the object and solve the collision by moving player back in its track so that he doesn't collide. You have the previous and the next coordinates, so, you have a vector along which to move the player back.
The difficulty of these steps depends greatly on shapes of objects in question, on maximum relative velocity of colliding objects, on required accuracy of collision solving. If you are dealing with primitives like rectangles and circles that can be relatively easy to implement, albeit there still will be difficult cases like collisions with several objects. Here are some basic tools you may want to look at:
getBounds
hitTestPoint
hitTestObject
Rectangle.intersects
Detecting and solving collisions is one of the most common problems in game development, so you may easily find tons of material on the subject on the net. For instance:
N Tutorial A - Collision Detection and Response
N Tutorial B - Broad-Phase Collision
Related
Hey everyone so I've been at this for awhile now and finally decided to ask for some help. So I am creating a game in AS3 where the object rotates in a circular motion from either left to right depending on the users mouse Presses. So I have some variables set up to act as friction. What I AM trying to accomplish is when the object is greater or less than a certain rotation degree I want the object to feel like it being pulled more and more to that side that it is currently at and the only way the object can say come back to its original position is if the use clicks on the mouse enough so their is no more force acting on it and say the speed increases at the same time for difficulty.
Here are the Variables I am currently working with:
//Variables
speed = 0.2;
vx = 0;
friction = 0.93;
maxspeed = 10;
I also have these buttons on stage so the user can click them to change the rotation of the objectlike so:
mainScreen.leftBtn.addEventListener(MouseEvent.CLICK, leftButtonClicked);
mainScreen.rightBtn.addEventListener(MouseEvent.CLICK, rightButtonClicked);
private function leftButtonClicked(e:MouseEvent):void
{
clickLeft = true;
clickRight = false;
}
private function rightButtonClicked(e:MouseEvent):void
{
clickRight = true;
clickLeft = false;
}
and I try to set up the mechanics in my ENTER_FRAME event listener like so:
//RIGHT = CLOCKWISE +, Left = COUNTER CLOCKWISE -
if (clickRight)
{
vx += speed;
moveSlow = true;
moveFast = false;
}else
if (clickLeft)
{
vx -= speed;
moveSlow = true;
moveFast = false;
}else
{
vx *= friction;
}
//IF object is Past 15 Degrees make object go faster MOVE FAST
if (object.rotation > 15)
{
moveFast = true;
moveSlow = false;
trace("MOVE_FAST");
}else
if (object.rotation < - 15)
{
moveFast = true;
moveSlow = false;
}else
{
vx *= friction;
}
object.rotation += vx;
//lumberJack.rotation += speed;
//boundaries of object
if (vx > maxspeed)
vx = maxspeed;
else if (vx < -maxspeed)
vx = -maxspeed;
I know I need to add something in the if object.rotation statement but not to sure what i also know i need to add something in the Mouse clicked event listeners to manipulate either speed or friction so the user can pull away from the force acting on it. I tried several time but still cant seem to figure it out. As of now the object rotates left or right depending on the users input and say the object is moving left and the user presses right the object slowly moves back to the left then returns to normal speed.
Please If anyone can help me figure this out I will greatly appreciate it!
To be honest I am having a little bit of trouble following your question, so this may only be a partial answer. I know, bad me, but I don't have enough stupid internet points to leave a comment, so this is all I can do to help, and I just can't care about the internet point system anymore.
First, for the love of insert deity, clean up all that unnecessary white space.
Here's a potential problem in your code: the rotation property of display objects returns a value between -180 and 180. This means that every 180 degrees, the rotation value changes sign. So you can't use whether the rotation is positive or negative to determine which direction the object is rotating in. That should be stored in a separate variable.
Another thing to consider: if moveSlow and moveFast are never true at the same time, you don't need to have two variables because it's redundant. You don't make use of those variables in your code above, but assuming you wrote this:
if (moveSlow) {
moveALittleBit();
} else if (moveFast) {
moveALot();
}
You could replace it with:
if (moveSlow) {
moveALittleBit();
} else {
moveALot();
}
Not only is that giving yourself unnecessary work, but it's bad because it means you can create "invalid states" (i.e. if you make a mistake and "moveSlow" and "moveFast" are both true at the same time).
Likewise, you shouldn't need separate variables for "clickedLeft" and "clickedRight" if they are both mutually exclusive. If they can both be false at the same time, however, you might be better off with something like:
clickDirection = "left";
clickDirection = "right";
clickDirection = "none";
If you went that route, you'd be better off using string constants instead of hardcoded strings, but I think that's getting too off-topic.
After reading your question many times, it sounds like maybe what you are looking for is momentum. Does this cover what you need?
if (clickRight)
{
vx += speed;
momentum = 0;
} else if (clickLeft)
{
vx -= speed;
momentum = 0;
} else
{
vx *= friction;
}
//IF object is Past 15 Degrees make object go faster MOVE FAST
if (object.degreesRotated > 15)
{
momentum += 1;
}else if (object.degreesRotated < - 15)
{
momentum -= 1;
}else
{
vx *= friction;
}
vx += momentum;
object.rotation += vx;
object.degreesRotated += vx; //remember how much we've rotated
//lumberJack.rotation += speed;
If object is a dynamic type, you can add the "degreesRotated" property whenever you feel like it. Otherwise, you might have to make a new class by extending whatever display type that object is, and add the degreesRotated field to that class
I'm new on here and needing a little help. Basically I'm designing a Pac-man like game which it has to be based on Wind in the Willows, so Mole-man it is!!
Anyway, I've done the basics of it, I've made my 2d mole, made him move, made him react to button presses etc. and now trying to get the walls to work, starting with the outer walls.
I made 4 rectangles around the edge, merged them to one shape and called it "outerWalls". I've been using hitTestObject to try and get it to work but with no success.
I did a test to make it work with moleman.xIncrement = -5 which makes him immediately move backwards without touching any walls. I am curious as to if this is because he is inside of the walls and it classes 4 outer walls as a square overall and as he is already inside the theoretical square moves him out? I am unsure. Have some code!!
Actions layer - frame 1:
import flash.events.KeyboardEvent;
var moleman = this.addChild(new Moleman_mc());
moleman.x = 100;
moleman.y = 200;
moleman.width = 24;
moleman.height = 24;
moleman.xIncrement = 5;
moleman.yIncrement = 0;
stage.addEventListener(KeyboardEvent.KEY_DOWN, doKeyDown);
function doKeyDown (e:KeyboardEvent):void
{
switch(e.keyCode)
{
case Keyboard.UP:
moleman.xIncrement = 0;
moleman.yIncrement = -5;
moleman.rotation = -90;
break;
case Keyboard.DOWN:
moleman.xIncrement = 0;
moleman.yIncrement = 5;
moleman.rotation = 90;
break;
case Keyboard.LEFT:
moleman.xIncrement = -5;
moleman.yIncrement = 0;
moleman.rotation = 180;
break;
case Keyboard.RIGHT:
moleman.xIncrement = 5;
moleman.yIncrement = 0;
moleman.rotation = 0;
break;
}
}
Actions layer - frame 2:
import flash.events.Event;
var collectCounter:int = 0;
moleman.x += moleman.xIncrement;
moleman.y += moleman.yIncrement;
if(moleman.hitTestObject(collect))
{
collectCounter ++;
collect.visible = false;
}
moleman.addEventListener(Event.ENTER_FRAME, wallHit);
function wallHit(event:Event):void
{
if(moleman.hitTestObject(outerWalls))
{
moleman.stop();
}
}
Actions layer - frame 3:
gotoAndPlay(2);
In addition, I cannot get the collect (e.g. collectables) to work, the plan would be to make it so each time you collect one "collect" item, that one disappears, you add one to collectCounter but all others stay visible. Currently if you collect one "collect" item, moleman just stops and stays where he is while another moleman appears and continues, not very useful. Any help on this would also be appreciated, not sure where to take it from there.
Any help is much appreciated, I am new at this but hoping it can go well :)
All help is much appreciated.
John.
In your case to detect collision you should use hitTestPoint method instead of hitTestObject. hitTestPoint method has the signature:
public function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean
so, when shapeFlag is set to true, it is possible to detect collision with the point (your hero) and maze shape (or any other arbitrary shape). This method is very powerful because the intersection with the real shape, not its bounding box is calculated. Modified wallHit method may look like this:
moleman.addEventListener(Event.ENTER_FRAME, wallHit);
function wallHit(event:Event):void
{
if(outerWalls.hitTestPoint(moleman.x, moleman.y, true))
{
// collision
moleman.x += -moleman.xIncrement;
moleman.y += -moleman.yIncrement;
moleman.xIncrement=0;
moleman.yIncrement=0;
}
moleman.x += moleman.xIncrement;
moleman.y += moleman.yIncrement;
if(moleman.hitTestObject(collect))
{
collectCounter ++;
collect.visible = false;
}
}
you may also remove frame 3 and "gotoAndPlay(2);" call because it is better to put the code which controls hero's position into ENTER_FRAME handler either.
Essentially I am trying to create a game, where the player has to dodge certain items, so far I have a piece of code that randomly adds 3 sharks to the stage.
The idea is that once the player has hit a shark he/she returns to the start location, I have an Action Script file that contains the speed,velocity etc of the shark, the every time the program is run the sharks will appear in a different location.
However, when I attempt to do a collision test of the sharks only one of the sharks respond, I cannot figure out how to make it that all 3 sharks effect the player (square_mc). Any help would be greatly appreciated.
//Pirate game, where you have to avoid particular object and get to the finish line to move onto the final level.
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveMode );
function moveMode(e:KeyboardEvent):void {
//movements for the pirate ship, this will allow the ship to move up,down,left and right.
if (e.keyCode == Keyboard.RIGHT) {
trace("right");
square_mc.x = square_mc.x + 25;
}
else if (e.keyCode == Keyboard.LEFT) {
trace("left");
square_mc.x = square_mc.x - 25;
}
else if (e.keyCode == Keyboard.UP) {
trace("up");
square_mc.y = square_mc.y - 25;
}
else if (e.keyCode == Keyboard.DOWN) {
trace("down");
square_mc.y = square_mc.y + 25;
}
}
//for.fla
//this program uses a for loop to create my Sharks
//a second for loop displays the property values of the sharks
function DisplayShark():void{
for (var i:Number=0;i<3;i++)
{
var shark:Shark = new Shark(500);
addChild(shark);
shark.name=("shark"+i);
shark.x=450*Math.random();
shark.y=350*Math.random();
}
}
DisplayShark();
for(var i=0; i<3;i++){
var currentShark:DisplayObject=getChildByName("shark"+i);
trace(currentShark.name+"has an x position of"+currentShark.x+"and a y position of"+currentShark.y);
}
//here we will look for colliosion detection between the two move clips.
addEventListener(Event.ENTER_FRAME, checkForCollision);
function checkForCollision(e:Event):void {
if (square_mc.hitTestObject(currentShark))
{
trace("The Square has hit the circle");
square_mc.x=50
square_mc.y=50 //these lines of code return the square back to it's original location
}
}
Just move your for loop into the ENTER_FRAME:
addEventListener(Event.ENTER_FRAME, checkForCollision);
function checkForCollision(e:Event):void {
for(var i=0; i<3;i++){
var currentShark:DisplayObject=getChildByName("shark"+i);
if (square_mc.hitTestObject(currentShark))
{
trace("The Square has hit the circle");
square_mc.x=50;
square_mc.y=50;
}
}
}
You can't just go through the for loop once and set the currentShark variable - you'll just end up testing against one shark every time you perform the collision test. Rather, every time you want to check collisions you must loop through all the sharks and do the collision testing.
I'm trying to have a rotatable line, controlled by the arrow keys. When you click the mouse, a ball drops from the cursor, and stops when it hits the line.
However, the balls always stop at the highest point of the line, across a line parallel to the x-axis.
My document class is as follows:
package
{
import flash.display.MovieClip;
import flash.events.*
import flash.display.Stage
import ball
public class Engine extends MovieClip
{
public static var stageRef:Stage
private static var leftKey:Boolean = false
private static var rightKey:Boolean = false
public static var pi = Math.PI
public static var lineRotate:Number = 0
public static var spinRate:Number = 60
public static var ground:line = new line()
public function Engine()
{
// constructor code
stage.addEventListener(Event.ENTER_FRAME, loop)
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler)
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
stage.addEventListener(MouseEvent.CLICK, addBall)
stageRef = stage
ground.x = 300
ground.y = 200
stage.addChild(ground)
}
private static function keyDownHandler(e:KeyboardEvent)
{
if (e.keyCode == 37) //left
{
leftKey = true
}
if (e.keyCode == 39)
{
rightKey = true
}
}
private static function keyUpHandler(e:KeyboardEvent)
{
if (e.keyCode == 37) //left
{
leftKey = false
}
if (e.keyCode == 39) //right
{
rightKey = false
}
}
public function loop(e:Event)
{
spin()
}
public static function addBall(e:MouseEvent) //adds ball
{
var tempBall:ball = new ball()
tempBall.x = e.stageX
tempBall.y = e.stageY
stageRef.addChild(tempBall)
}
private static function spin() //spins the "ground" line
{
if (leftKey) // minus
{
lineRotate -= spinRate
}
if (rightKey) // plus
{
lineRotate += spinRate
}
ground.rotation = lineRotate * (pi / 180) //convert to radians
}
}
}
The class for the ball is as follows:
package
{
import flash.display.MovieClip;
import flash.events.*
public class ball extends MovieClip
{
public var vX:Number = 0
public var vY:Number = 2
private var gravity:Number = 0
public function ball()
{
// constructor code
addEventListener(Event.ENTER_FRAME, loop)
}
public function loop(e:Event)
{
this.x += vX
this.y += vY
this.vY += gravity
if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
{
removeSelf()
}
if (Engine.ground.hitTestObject(this))
{
trace('yep')
stopBall()
}
else
{
trace('nope')
}
}
public function removeSelf()
{
removeEventListener(Event.ENTER_FRAME, loop)
this.parent.removeChild(this)
}
public function stopBall()
{
gravity = 0
vY = 0
vX = 0
}
}
}
I've uploaded my .swf to here.
The easiest thing you can do, for balls is a hitTestPoint with the third argument as true, that turns on shape detection.
ground.hitTestPoint(x,y,true);
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#hitTestPoint()
Ok, so, hm, you can check a single point on the ball, like it's bottom point, or you can check an array of points along the bottom of the ball for easier precision... this is the quickest way to do this if you're not planing anything more complex then this. If, however, you want to create a full fleshed game, leave it to a 2d physics library like http://www.box2dflash.org/.
Forget skinners collision detection for a bigger game(something small like this could survive with it though), as it is bitmap based and would kill performance much more then box2D, it's a nice mask example, but it not a good idea to use for performance reasons.
I've changed your code a little bit. I've hittested a bunch of points on the bottom part of the ball, bear in mind that the ball's 0,0 inside the movieclip is centered on the balls center.
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.geom.Point;
public class ball extends MovieClip
{
public var vX:Number = 0;
public var vY:Number = 2;
private var gravity:Number = 0;
public function ball()
{
// constructor code
addEventListener(Event.ENTER_FRAME, loop);
}
public function loop(e:Event)
{
this.x += vX;
this.y += vY;
this.vY += gravity;
if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
{
removeSelf();
}
/*we will now check something like 18 points on the bottom side of the ball for colision
instead of just the bottom, you can probably guess why... if you cant, replace i on line
43 (var angleInRadians...) with 270 to test and then drop balls on a slopped ground surface... of course
you should definitely juse a physics engine like http://www.box2dflash.org/ for anything more complex.
*/
for (var i:int = 180; i<=360; i+=10)
{
/*keep in mind that ball.rotation property has 0 at the top of the ball, while here for these we are using the standard
Cartesian coordinate system. The effect of this on rotation would be that it is +90 and for X(yes, X) it would be Math.SIN(),
not Math.COS()!!!, and for Y it would be Math.sin() with a minus in front because of the flash coordinate system y axis rotation.
It's not really related to this, but it's a point that has anoyed me to no end in trig related stuff in flash when I was starting.
*/
var angleInRadians:Number = i / 180 * Math.PI;
var posX:Number = this.x + Math.round(Math.cos(angleInRadians) * width / 2);
var posY:Number = this.y - Math.round(Math.sin(angleInRadians) * height / 2);//minus because y in flash is upside down
Engine.ground.hitTestPoint(posX,posY, true);
if (Engine.ground.hitTestPoint(posX,posY, true))
{
stopBall();
}
else
{
}
}
/*if (Engine.ground.hitTestObject(this))
{
trace('yep')
stopBall()
}
else
{
trace('nope')
}*/
}
public function removeSelf()
{
removeEventListener(Event.ENTER_FRAME, loop);
this.parent.removeChild(this);
}
public function stopBall()
{
gravity = 0;
vY = 0;
vX = 0;
}
}
}
Unrelated to this above, you need to rethink your OOP a little bit, you are doing things a little bit wrong :). A project this small would cope, but anything bigger would give you headaches. Don't take this as an attack, I want to try to guide you to the "right" path and show you the logical fallacies in your code, I am not mentioning this to "pwn" you cause your oop is bad :).
For example, gravity, inside class ball? Yikes... what if you wanted to have gravity for ninjas, shurikens, ragdolls, etc? Are you going to subclass Ninjas out of the ball class?
Ninja extends ball? That would be fun.
Do you think that there might be a better place for that gravity thing? Is "gravity" a property of the ball at all(err, tehnically it is, but not this homogeneous gravity you are putting here, it's more like a permeating, all present thing, cause there is some huge body that we are too close to)? It should be in the Engine...
Also, you did Engine.ground thing... now, this is a static variable... this is another bad thing, very bad thing :)
The reason is similar to the previous example, but a little bit turned around. What if you want to reuse ball inside, say Engine2? Or Crytek engine? or UDK? I think it might be sligtely problematic, don't you think? You would have to go in and change the ball class... Imagine if every code you used forced you to do that...
Basically, you probably could have done something like this:
var bla:ball = new ball(ground);
this is better, muuuch better... now we can use it in crytek, udk and Engine2 easily...
Technically, you want to avoid things like static variables of this kind in your code, the idea is called encapsulation. The idea is that you hide the internal workings of classes from other classes that are unrelated to it or should not know about it. The less you NEED to know, the more portable, reusable (yada yada) your classes are. Engine.ground is a huge encapsulation breaker because ball has absolutely zero need to know about class Engine, it should only be concerned with a reference to the ground itself...
In time you will learn all of this, patterns in programming, and particularly , which patterns save time, and which are appalling, and why (static variables have their use, but only for ultra simple stuff, avoid Singletons as well if you know what they are).
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)
Sorry for the headache I've caused you...
Remember, use a physics engine for more complex code, don't reinvent the wheel(reuse everything for your projects that is good for them/sufficient for them)... It's fun to test stuff like this, but other folks have done it better before you, and unless you specifically need to delve into their domain, you don't need to concern yourself with every little detail. You are probably trying to build games/something, not bother with Newtonian dynamics...
HitTest is just fine for simple stuff though, but this is bordering on simple... :)we are already slightly into physics here, and you might find you need a more robust solution soon...
So generally, try to make your code "robust" by thinking about encapsulation and dependencies between your classes. If a dependency feels unatural (ie ball depends on Engine) then something is probably wrong, and will break later, will break for another game or engine or... I'll end it here...
Take care and have fun.
According to the article Collision detection methods: hit-test and hit-testobject alternatives, that is by design:
As you can see Flash uses the bounding
boxes of objects - it takes the
highest and lowest possible points,
the leftmost and rightmost points and
draws a rectangle around them.
The author mentions several alternatives to workaround this limitation, such as using hitTestPoint or Grant Skinner's Shape based collision detection.
So in my game, every 'enemy' movieclip creates a textfield that represents the name of the enemy. The problem is, I want my collision detect engine to detect a collision with the enemy itself, not the textfield.
This is the code that I have currently have on my collision class for detecting a collision with the enemy:
for(var i = 0;i < _enemies.length; i++)
{
if(CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
}
else if(CollisionEngine.isColliding(_laser, _enemies[i], _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
}
The first if clause checks for a collision with the enemy's text field. The else if checks for a collision with the enemy.
The problem with this current implementation is that if the 'laser' hits the textfield first, passes through it, and hits the enemy, it's not detected as a collision.
Any idea on how I could work around this?
Change the order of the checks:
if(CollisionEngine.isColliding(_laser, _enemies[i], _animation))
{
if(!CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
}
Note that the above code is equivalent to:
if(CollisionEngine.isColliding(_laser, _enemies[i], _animation) && !CollisionEngine.isColliding(_laser, _enemies[i]._enemyNameTextField, _animation))
{
_enemies[i].kill();
_animation._killedEnemy = true;
}
Alternatively, you can explicitly define a hitArea on all of your enemies so that collisions with the text field aren't considered collisions.
in your enemy have a method like:
public function getCollision():Sprite{
//Where this.body is the main animation for the clip/Hittest area
return this.body
}
or something that returns just the active hit area, then you can run:
if(CollisionEngine.isColliding(_laser, _enemies[i].getCollision(), _animation))