I'm working on a simple game in AS3 where a player should be able to jump on a box.
How to detect if the player landed on the box and didn't run into it?
AS3 to make the player jump:
var grav:Number = 10;
var jumping:Boolean = false;
var jumpPow:Number = 0;
stage.addEventListener(KeyboardEvent.KEY_DOWN, onDown);
stage.addEventListener(Event.ENTER_FRAME, update);
function onDown(evt:KeyboardEvent):void
{
if(evt.keyCode == Keyboard.UP)
{
//umbau das mehrfach tab bis höhe erreicht?
if(jumping != true)
{
jumpPow = -10;
jumping = true;
}
}
}
function update(evt:Event):void
{
if(jumping)
{
player_mc.y += jumpPow;
jumpPow += grav;
if(player_mc.y >= stage.stageHeight)
{
jumping = false;
player_mc.y = stage.stageHeight;
}
}
}
This is how the game layout looks like: (the grey box is moving from right to left, the player's position is fixed)
You should start with a simple rectangle collision function:
/* Test collision between a rectangle and another rectangle. */
/* right() and bottom() return x+width and y+height of the parent rectangle. */
Rectangle.prototype.collideRectangle=function(rectangle_){
if (this.x>rectangle_.right()||this.y>rectangle_.bottom()||this.right()<rectangle_.x||this.bottom()<rectangle_.y){
return false;
}
return true;
}
if you are using the gray rectangle as a platform, you can do a simple test to see if the red rectangle is landing on the gray one by using its current and last position:
/* If true, the two rectangles are definitely colliding. */
if (red_rect.collideRectangle(gray_rect)){
/* Get the last position of the bottom of the red rectangle. */
var old_bottom=red_rect.bottom()-red_rect.velocity.y;
/* If the old bottom was above the gray rectangle on the last frame and is colliding in this frame, you know that the red rectangle is landing on the gray rectangle. */
if (old_bottom<gray_rect.y){
/* Place the red rectangle on top of the gray rectangle. */
red_rect.y=gray_rect.y-red_rect.height;
}
}
This is assuming you move the red rectangle by adding the value of velocity.y to its y position on each frame. Also, I'm using JavaScript, but AS3 and JavaScript aren't so different.
If you want more than a platform, and you want full collision, you should consider the penetration depth of the red rect on the gray rect and separate them on the axis with the smallest penetration depth. There are a lot of resources about this stuff online, and AS3 in particular has a lot of game related tutorials with a focus on do it yourself physics. Some really good collision methods are separation of axes theorem (SAT) and GJK (I'm not going to try spelling the names in GJK).
Related
So I'm having some issues with ActionScript. I'm creating a project where you are able to click on a penny and drag it to a scratch card, where you then scratch off the coating and text is revealed.
Everything is composed of two layers. The bottom layer is an image of the card with the text on it( I turned it into a bitmap). The top layer is a rectangle (scratch off coating) with a solid fill that I turned into a movie clip with a property name of maskee3. So far I was successful.
Where I ran into problems is when I tried adding another scratch card to the stage. I did the same process, turned the card into a bitmap and the rectangle scratch off coating (top layer) into a movie clip with a property name of maskee4. When I hit preview movie it still only scratches off the initial card. I honestly don't know what to do and how to fix this to make it scratch all of them.
Below is the coding I did, I have just been shooting in the dark at this point. Any help would be appreciated. Thanks!
/* Drag and Drop
Makes the specified symbol instance moveable with drag and drop.
*/
penny.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void
{
penny.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void
{
penny.stopDrag();
}
//start code for scratch off//
/*************************
MASKING
**************************/
/*
Initial variables
*/
// Change lineSize to control size of your eraser
var lineSize:Number=4;
// doDraw is true when user's mouse is down
var doDraw:Boolean=false;
// resumeDrawing is true when user drags their mouse off stage while drawing
var resumeDrawing:Boolean=false;
/*
Create a bitmap to act as our image mask
Add it to stage & cache as bitmap
*/
var erasableBitmapData:BitmapData = new BitmapData(400, 400, true, 0xFFFFFFFF);
var erasableBitmap:Bitmap = new Bitmap(erasableBitmapData);
erasableBitmap.cacheAsBitmap = true;
addChild(erasableBitmap);
/*
Set the erasable bitmap as a mask of our image & cache image as bitmap
*/
maskee3.cacheAsBitmap = true;
maskee3.mask = erasableBitmap;
maskee4.cacheAsBitmap = true;
maskee4.mask = erasableBitmap;
/*************************
ERASER
**************************/
/*
Create a sprite for drawing the eraser lines onto
Set its lineStyle, and move the line to the current mouse x,y
*/
var eraserClip:Sprite = new Sprite();
initEraser();
function initEraser():void{
eraserClip.graphics.lineStyle(lineSize,0xff0000);
eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
}
/*
Create a bitmap to copy the erased lines to.
This is required to ensure a smooth erase effect (applying eraserClip
directly to erasableBitmapData leaves jaggy edges around alpha areas.
If anyone knows why, I'd love to know!)
*/
var drawnBitmapData:BitmapData = new BitmapData(400, 400, true, 0x00000000);
var drawnBitmap:Bitmap = new Bitmap(drawnBitmapData);
/*************************
MOUSE EVENTS
**************************/
/*
Add event listeners for mouse movements
*/
stage.addEventListener(MouseEvent.MOUSE_MOVE,maskMove);
stage.addEventListener(MouseEvent.ROLL_OUT, maskOut);
stage.addEventListener(MouseEvent.ROLL_OVER,maskOver);
stage.addEventListener(MouseEvent.MOUSE_DOWN,startDrawing);
stage.addEventListener(MouseEvent.MOUSE_UP,stopDrawing);
/*
Mouse down handler
Begin drawing
*/
function startDrawing(e:MouseEvent):void {
eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
doDraw=true;
}
/*
Mouse up handler
Stop drawing
*/
function stopDrawing(e:MouseEvent):void {
doDraw=false;
resumeDrawing = false;
}
/*
Mouse out handler
If user was drawing when they moved mouse off stage, we will need
to resume drawing when they move back onto stage.
*/
function maskOut(e:Event):void {
if (doDraw){
resumeDrawing = true;
}
}
/*
Mouse over handler
If user's mouse if still down, continue drawing from the point where
the mouse re-entered the stage.
*/
function maskOver(e:MouseEvent):void {
if (resumeDrawing){
resumeDrawing = false;
eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
}
}
/*
Mouse move handler
*/
function maskMove(e:MouseEvent):void {
if (doDraw && !resumeDrawing){
// Draw a line to current mouse position
eraserClip.graphics.lineTo(stage.mouseX,stage.mouseY);
// Clear the drawn bitmap by filling it with a transparent color
drawnBitmapData.fillRect(drawnBitmapData.rect, 0x00000000);
// Copy our eraser drawing into the erasable bitmap
// (This is required to ensure the smooth alpha edges on our eraser are retained)
drawnBitmapData.draw(eraserClip , new Matrix(), null, BlendMode.NORMAL);
// Fill the erasable bitmap with a solid color
erasableBitmapData.fillRect(erasableBitmapData.rect, 0xFFFFFFFF);
// Copy the scribble bitmap to our main bitmap, with blendmode set to ERASE
// This erases the portion of the mask that has been drawn.
erasableBitmapData.draw(drawnBitmap, new Matrix(), null, BlendMode.ERASE);
}
// Update after event to ensure no lag
e.updateAfterEvent();
}
/************************RESET BUTTON
**************************/
reset_btn.addEventListener(MouseEvent.CLICK,reset);
function reset(e:Event):void {
eraserClip.graphics.clear();
initEraser();
erasableBitmapData.fillRect(erasableBitmapData.rect, 0xFFFFFFFF);
}
One "masker" has single "maskee" only.You can merge maskee if you prefer keep it single masker.
Simply replace this.
maskee3.cacheAsBitmap = true;
maskee3.mask = erasableBitmap;
maskee4.cacheAsBitmap = true;
maskee4.mask = erasableBitmap;
by this:
var container:Sprite = new Sprite();
container.addChild(maskee3);
container.addChild(maskee4);
container.cacheAsBitmap=true;
container.mask = erasableBitmap;
addChild(container);
Worked with flash cs6 and as3.
I wanted to make menu like this link. When mouse_over on the menu, the blue rectangle moves to the right; and when mouse_up, the animation reversed.
I made the blue rectangle in a movieclip menuBlueHome. There, I made the rectangle moves from left to right (from frame 1 to 10). At frame 10, I made action script:
stop();
I was still working with home menu when I faced this problem. When I hover the home menu, the blue rectangle moves to the right and reversed straightaway before mouse_up. Here is the code outside the mc:
var menuBlueHome: MovieClip;
menuBlueHome.stop();
var direct: String;
btnHome.addEventListener(MouseEvent.MOUSE_OVER,onOverHome);
btnHome.addEventListener(MouseEvent.MOUSE_OUT,onLeaveHome);
btnHome.addEventListener(MouseEvent.CLICK,onClickHome);
function onOverHome(e:MouseEvent):void{
androidHome.visible = true;
menuBlueHome.play();
}
function onLeaveHome(e:MouseEvent):void{
androidHome.visible = false;
addEventListener(Event.ENTER_FRAME,onFrameHome);
}
function onClickHome(e:MouseEvent):void{
gotoAndStop(1);
}
function onFrameHome(event:Event):void {
if(menuBlueHome.currentFrame > 9) {
direct = "backward";
}
var backAmount:Number = menuBlueHome.currentFrame -1;
if(direct == "backward") {
menuBlueHome.gotoAndStop(backAmount);
}
}
Did I make something wrong with the code? Thanks for your help.
Try with this code:
function onLeaveHome(e:MouseEvent):void{
androidHome.visible = false;
menuBlueHome.removeEventListener(Event.ENTER_FRAME, onFrameHome);
menuBlueHome.addEventListener(Event.ENTER_FRAME, onFrameHome);
}
function onFrameHome(event:Event):void {
var backAmount:Number = menuBlueHome.currentFrame - 1;
menuBlueHome.gotoAndStop(backAmount);
if(backAmount == 1) menuBlueHome.removeEventListener(Event.ENTER_FRAME, onFrameHome);
}
Here you have and example.
But, I recommend you to do your code more dynamic, here you have another example.
So I created a nice collision system shown here. Now I have my own character sprite, which has messed up the collision on all sides.
edit: because people are misunderstand what I want, I WANT it to overlap on the bottom and top, it gives it a 3D effect. My problem is that it's colliding incorrectly with the bitmap
I've tried using the pixel perfect collision system but I have a problem with it:
It only detects collision right at the edge, as you can see in the video, the ball can go slightly in front and behind the wall like it wasn't just a flat plane.
code responsible for the current collision (it did have some other stuff but that's been removed):
for each (var wall in Walls)
{
if (wall.hitTestPoint(Character.x, Character.y, true)) //col right
{
Character.x+=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x, Character.y, true)) //col left
{
Character.x-=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x , Character.y, true)) //col bottom
{
Character.y+=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x, Character.y, true)) //col top
{
Character.y -= CharacterSpeed;
}
}
That is correct (and I mean that's what the code does) and that effect depends on framerate as well.
The principle is easy to understand. ex: You move an object by 5 pixels, the colliding object is 3 pixels away, when you test for collision you have an overlap of 2 pixels. To correct this you need what's called a TOI (time of impact) algorithm.
As of right now there's no fixing your code because you apply a motion is the inverse way it's supposed to work. You move first then you test while the correct way is you test then you move. For example:
you are about to move the object by x pixels.
test if the object + x pixels will collide.
if it will collide calculate by how much you should move it then move it by x pixels - corrected pixels.
if it will not collide then move it by x pixels.
As you see you do the opposite:
move by x pixels.
test for collision.
The result is overlapping objects.
What you can do as a hack without changing your code is calculate the overlapping and then correct the object position.
you can try draw your objects to a BitmapData using BitmapData.draw method, and than use BitmapData.hittest, here a sample:
import flash.display.BitmapData;
import flash.geom.Point;
var myBitmapData:BitmapData = new BitmapData(100, 80, false, 0x00CCCCCC);
var mc_1:MovieClip = this.createEmptyMovieClip("mc", this.getNextHighestDepth());
mc_1.attachBitmap(myBitmapData, this.getNextHighestDepth());
var mc_2:MovieClip = createRectangle(20, 20, 0xFF0000);
var destPoint:Point = new Point(myBitmapData.rectangle.x, myBitmapData.rectangle.y);
var currPoint:Point = new Point();
mc_1.onEnterFrame = function() {
currPoint.x = mc_2._x;
currPoint.y = mc_2._y;
if(myBitmapData.hitTest(destPoint, 255, currPoint)) {
trace(">> Collision at x:" + currPoint.x + " and y:" + currPoint.y);
}
}
mc_2.startDrag(true);
function createRectangle(width:Number, height:Number, color:Number):MovieClip {
var depth:Number = this.getNextHighestDepth();
var mc:MovieClip = this.createEmptyMovieClip("mc_" + depth, depth);
mc.beginFill(color);
mc.lineTo(0, height);
mc.lineTo(width, height);
mc.lineTo(width, 0);
mc.lineTo(0, 0);
return mc;
}
I'm trying to make a pseudo 3D collision with multiple walls. It works to an extent:
I can collide with the sides of each wall
but only on one of the walls, if I were to go to the top, It goes behind the rectangle like this but on the second rectangle, I go above it like this I don't why this is happening (I am a beginner in AS3). Using trace, it says that the player is at layer 0 while the wall is at layer 2, but the ball still goes above the wall.
edit: I'm using flashdevelop incase that changes anything
Edit: Changed code, it works fine, but ignores the second wall, it prints it's name but doesn't change the boolean
for each (var wall in WallsList)
{
trace(wall.name)
if (wall.hitTestPoint(Character.x - hitRad+.995, Character.y, true)) //col right
{
Character.x+=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x + hitRad-.995, Character.y, true)) //col left
{
Character.x-=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x , Character.y- hitRad+25, true)) //col bottom
{
Character.y+=CharacterSpeed;
}
if (wall.hitTestPoint(Character.x, Character.y+hitRad-25, true)) //col top
{
Character.y -= CharacterSpeed;
}
if (wall.hitTestPoint(Character.x, Character.y+hitRad, true)) // col top #2
{
trace(wall.name) // prints for both walls
Top = true; // only changes for 1 wall
}
if (!wall.hitTestPoint(Character.x, Character.y+hitRad, true))
{
Top = false;
}
}
Bring wall object above the ball:
setChildIndex(wallObject, this.numChildren - 1);
The objective is for the red circle move horizontally to the left and when completely off stage to reappear on the right side.
Why is it that setting the property X in ball.graphics.drawCircle (300, 300, 50); causes the ball to disappear before it reaches the stage left side?
If I leave x being 0 it behaves as I want
Class.
public function RedBall()
{
ball.graphics.beginFill (0xFF0000);
ball.graphics.drawCircle (300, 300, 50);
ball.graphics.endFill();
}
In my main
function update(e:Event):void {
a.ball.x -= 10;
if (a.ball.x < -a.ball.width) {
a.ball.x = stage.stageWidth + a.ball.width;
}
}
I think this probably must be some very obvious errors as I'm beginning to learn AS3 but if needed I can post the whole code.
You are drawing your circle at a point of 300,300, but you are not later accounting for that in your code where you check its position.
You can do one of two things.
Recommended:
Draw the circle at the point of origin, and place the instance of ball at an x of 300.
Alternative:
Use the ball's actual bounds to determine its position:
function update(e: Event): void {
ball.x -= 10;
if (ball.getBounds(stage).x < -ball.width) {
ball.x = stage.stageWidth - ball.getBounds(stage).x;
}
}