Find out what object caused the hit test and compare position to character in AS3 - actionscript-3

I have been searching far and wide for this answer and I haven't found an answer to my question because my objects are not in an array, but in a movieclip on the stage. I'm trying to detect which object caused the collision so that I use the objects to restrict the movement of the character. Looking at the image below all of the bushes, the house, and a few dozen other objects are going to be used as blocking objects.
I have found a way to detect the collision with the movieclips children objects, but not really which object is making this collision. I've tried looping through each object everytime a movement takes place, and testing if there was a collision, but it never registers any hits.
Here is my current code that is registering hits.
var background = bGround; // Make reference to the BG in the mainActions
var bgObjects = bGround.bgElements;
//trace(bgObjects.numChildren);
if(bgObjects.hitTestPoint(character.x,character.y,true)) {
trace("character: " + character.x + "," + character.y);
trace("position of hit " + bgObjects.x + "," + bgObjects.y);
}
This is nested inside of a eventlistener that watches for key presses so it re-tests every button press, or every time the user moves the character. I thought looping through the objects after the hit test happens was the best idea, or during each movement loop through and test that object in particular, but couldn't get either working. Here is one attempt.
for (var i : Number = 0; i < bgObjects.numChildren; i++) {
var instBgElem = bgObjects.getChildAt(i);
if(character.hitTestPoint(instBgElem.x, instBgElem.y, true)) {
// Check X Left
if (character.x < bgObjects.getChildAt(i)).x {
trace("hit left");
}
// Check X right
if ( character.x > bgObjects.getChildAt(i)).x {
trace("hit right");
}
// Check Y top
if ( character.y < bgObjects.getChildAt(i)).y {
trace("hit top");
}
// Check Y Bottom
if ( character.y > bgObjects.getChildAt(i)).y {
trace("hit bottom");
}
}
}
EDIT : I was able to get one piece of code working to an extent, but it really doesn't test where the hit test occurs on the background object. I may need to find the bounding box of each object and figure out which side of the bounding box is being hit, accepting all help here.
for (var i : Number = 0; i < bgObjects.numChildren; i++) {
var instBgElem = bgObjects.getChildAt(i);
if(instBgElem.hitTestPoint(character.x,character.y,true)){
if(instBgElem.x > character.x) {
trace("right");
} else if(instBgElem.x < character.x) {
trace("left");
}
if(instBgElem.y > character.y) {
trace("up");
} else if(instBgElem.y < character.y) {
trace("down");
}
}
}
EDIT 2: The problem is figuring out where the hit test comes in contact with the character so that I can restrict that movement. I'm not sure how to do that, I have been experimenting with bounding box.
var charBounding = character.getBounds(this);
trace("character bounding box" + charBounding);
trace("character bottom: " + charBounding.bottom);
trace("character top:" + charBounding.top);
trace("character left:" + charBounding.left);
trace("character right:" + charBounding.right);
That outputs character and updates it whenever the character moves, don't know how to use this to my advantage.
character bounding box(x=85.95, y=79.05, w=72.2, h=72.2)
character bottom: 151.25
character top:79.05
character left:85.95
character right:158.15

Related

AS3 panning very slow in only one direction and only with Adobe AIR

I have a couple dozen collision detections going on each frame as well as a couple dozen objects panning as the character moves left or right (but only if near the edge of the screen). It actually works perfectly when publish in FlashPlayer21 but if I use Adobe AIR, it goes super slow. This only happens when the character is going right and the background is moving left (so the "camera is panning right"). When the character goes left, it is super smooth. I'm using nearly identical code for both with the exception of a different sign (+/-) here or there. Maybe I shouldn't worry about this, or maybe I need to test on my iphone or a tablet but since I don't know how to do that yet... isn't that the whole point of Adobe having these AIR for desktop and internal iOS mobile device simulators?
Here's the relevant code:
private function panScreen2(c:Runner,speedFac:Number,...depthSets):void{
if (c == char){ // if the c:Runner object is the character in play
// I use drivers to cause objects that can only be displayed on every 8th pixel to still recieve floating point number values for animation
var cDepth: int = c._drives.parent.parent.getChildIndex(c._drives.parent);
// "camera" pans to left as character goes left
if (c.x <= 500 && c._drives._facingRight == false && c._speed !=0)
{
for (var i:int = depthSets.length-1; i >= 0; i--){
for (var j:int = depthSets[i].arr.length-1; j >= 0; j--){
var depthDif:int = -cDepth+depthSets[i].arr[j].parent.parent.getChildIndex(depthSets[i].arr[j].parent)
depthSets[i].arr[j].x -= (c._speed)*depthDif*depthSets[i].ratio-speedFac;
}
}
// "camera" pans to right as character goes right sending objects to left
} else if (c.x >= 1000 && c._drives._facingRight == true && c._speed !=0)
{
for (var i:int = depthSets.length-1; i >= 0; i--){
for (var j:int = depthSets[i].arr.length-1; j >= 0; j--){
var depthDif:int = -cDepth+depthSets[i].arr[j].parent.parent.getChildIndex(depthSets[i].arr[j].parent)
depthSets[i].arr[j].x -= (c._speed)*depthDif*depthSets[i].ratio+speedFac;
}
}
} else
{
c.x += c._speed;
}
}
}
and then I call the function like so:
panScreen(c,{arr:allBlocks,ratio:1},{arr:allDepth,ratio:1},{arr:allBears,ratio:1});
depthSets are the extra parameters I pass when calling the function. It is an object which consists of an array and a ratio. This function iterates through the array (actually Vector, if that matters) and moves everything to the right or left by an amount determined by the ratio and the speed of the character. Pretty straight forward, and again, it works like a charm in Flash Player 21 and works smooth as butter when the character goes leftward which sends all the objects to the right as a "camera" pan. So what's going on with trying to go to the right?

AS3 Change position of object in TileList

I am using a tileList in ActionScript 3 to display movieclips. However I have the problem that not all reference points of the movieclips are in the correct place. This leads to that these movieclips are shown partly outside of their cell in the tileList.
I have tried to adjust the x and y position of the movieClip before adding it to the tileList, but this did not change anything. Now I have tried to find if it is possible to change the x and y position of an object already in the tileList, but without finding any answers.
I hope that I have made my problem clear.
Thanks in advance!
EDIT:
This is the code I tried:
private function initTileList():void {
for(var i:int = 0; i < _movieClips.length; i++) {
changePos(_movieClips[i]);
tileList.addItem({label: _movieClips[i].name, source: _movieClips[i]});
}
}
private function changePos(mc:MovieClip):void {
if(MovieClip(mc).getRect(mc).x != 0) {
mc.x -= MovieClip(mc).getRect(stateMachineRef).x;
}
if(MovieClip(mc).getRect(mc).y != 0) {
mc.y -= MovieClip(mc).getRect(stateMachineRef).y;
}
}
I do not have any errors, it just doesn't affect the position of the object in the tileList.
Example of how the problem looks.
Hard to say where's the problem without knowing these things:
1. What tileList.AddItem() does exactly;
2. What is stateMachineRef
3. How MovieClips are loaded. If they are loaded from a network, that'll be a whole different story.
By the way, you don't have to cast MovieClip(mc) as mc is already a MovieClip. Also, there is no difference as to when you will correct the coordinates: before or after adding to the tileList. Should work either way.
So, given that information on your problem is not complete, I would just suggest you insure the following steps:
-We assume all tiles are displayed inside a tile container. It can be Stage or a MovieClip or any suitable DisplayObjectContainer, so let's call it just tileContainer from now on.
-We assume all tiles are of the same width and height. If you are not sure, you should check it again.
-We assume that each tile in the tileContainer is displayed at some regular grid coordinates. I.e. it conforms the following code:
for (var pos_y:int = 0; pos_y < GRID_SIZE_Y; pos_y++) {
for (var pos_x:int = 0; pos_x < GRID_SIZE_X; pos_x++) {
var tile:Tile = getNextTile(); // just get a tile from somewhere
tile.source.x = pos_x * TILE_WIDTH; // using your tile structure
tile.source.y = pos_y * TILE_HEIGHT;
tileContainer.addChild(tile.source);
}
}
Now I see your problem that some tiles are created in a way that they have their source movieclip coordinates shifted from (0,0). So they will not align with the grid.
What are you doing seems to be a proper way of aligning them but I don't know exactly what happens in your code so I'll just rewrite it:
function changePos(mc:MovieClip) {
var r:Rectangle = mc.getRect(mc);
mc.x -= r.x; // note you don't need any if's
mc.y -= r.y;
}
And in the above loop just add the changePos() AFTER setting the grid coordinates:
tile.source.x = pos_x * TILE_WIDTH;
tile.source.y = pos_y * TILE_HEIGHT;
changePos(tile.source);
tileContainer.addChild(tile.source);
If you're following all these steps, that's basically all you need and it will work for sure.

ActionScript 3.0 HitTestObject on an Array

Good afternoon everyone!
I have a small problem in ActionScript 3.0 with hitTestObject.
I would like to chechk if my character hits a platform (I'm making a simple platform game.).
I have a platform object exproted for action script , and i add childs frim this to an array.
Until this point everything goes well, i can put them on stage etc.
I have written a cycle to chechk if my caharacter hits the platform but it doesn't works correctly. My character falls throught the first platforms and only stop's falling when it hits the last platform. (So for the last in the array it works well.)
And now here is this part from my code, i hope someone can help me with it. :)
import flash.events.Event;
import flash.geom.Rectangle;
stop();
var vy:Number=0;
var gv:Number=1;
var sebesseg:Number=4;
var jumped:Boolean=false;
var stay:Boolean=false;
var level:Array=new Array ;
var gravity:Number=2;
var velocity:Number=1.1;
var platform0:MovieClip=new platform ;
level.push(addChild(platform0));
level[0].x=200;
level[0].y=450;
var platform1:MovieClip=new platform ;
level.push(addChild(platform1));
level[1].x=700;
level[1].y=650;
var platform2:MovieClip=new platform ;
level.push(addChild(platform2));
level[2].x=1000;
level[2].y=800;
stage.addEventListener(Event.ENTER_FRAME, cameraFollowCharacter);
function cameraFollowCharacter(evt:Event) {
root.scrollRect=new Rectangle(PORK1_mc.x-(stage.stageWidth/2)+320,PORK1_mc.y-(stage.stageHeight/2)-50,stage.stageWidth,stage.stageHeight);
}
stage.addEventListener(KeyboardEvent.KEY_DOWN,gomb);
function gomb(k:KeyboardEvent):void {
trace(k);
if (k.keyCode==37) {
if (sebesseg==2) {
sebesseg=sebesseg+0;
} else {
sebesseg-=1;
}
} else if (k.keyCode==39) {
sebesseg+=1;
} else if (k.keyCode==Keyboard.ESCAPE) {
stop();
root.scrollRect=new Rectangle (stage.x,stage.y,stage.stageWidth,stage.stageHeight);
gotoAndStop(2);
} else if (k.keyCode==Keyboard.F1 && stay==false) {
stage.frameRate=0;
stay=true;
} else if (k.keyCode==Keyboard.F1 && stay==true) {
stage.frameRate=24;
stay=false;
}
}
stage.addEventListener(Event.ENTER_FRAME, megy);
function megy(e:Event):void {
PORK1_mc.x+=sebesseg;
gravity*=velocity;
PORK1_mc.y+=gravity;
//trace(velocity);
}
THIS PART IS THE PROBLEM, HITTESOBJECT ONLY WORKS FOR THE LAST PLATFORM
PORK1_mc.hitPork_mc this is my character (hitPork_mc is an invisible rectangle for better HitTestObject. So as i wrote my char. falls throught the platfroms until the last one, he falls on tha last and stops falling, so for the last platform in the array it works perfectly.
I wouldn't like to change a lot on my code, only on the hitTest part if its possible.
this.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
function handleEnterFrame(e:Event):void {
for (var i:int = 0; i < level.length; i++) {
if (level[i].hitTestObject(PORK1_mc.hitPork_mc)) {
velocity=0;
} else {
velocity=1.1;
gravity=4;
}
}
}
Thanks for every help in advance!
With your original code, you do the following for each platform:
If there is a collision, set velocity to zero.
If not, set velocity to 1.1.
Since you do this in order, here's what happens when you collide with platform 1 but not 2 or 3:
The collision with 1 sets your velocity to zero.
The collision with 2 sets your velocity to 1.1.
The collision with 3 sets your velocity to 1.1.
Since these happen all in a row in the same frame, the end result is that your character never stops unless the collision is with platform 3.
By returning after setting velocity to zero, you are breaking out of the loop, preventing your collision work from being undone. What you are doing will work, but you can also do it another way which is less concise but may make more sense:
function handleEnterFrame(e:Event):void
{
var collided:Boolean = false; //This will record if you collided or not.
for (var i:int = 0; i < level.length; i++)
{
if (level[i].hitTestObject(PORK1_mc.hitPork_mc))
{
collided = true;
}
}
if(collided) velocity = 0;
else
{
velocity=1.1;
gravity=4;
}
}
Note that here we're not applying velocity and gravity inside the for loop because we don't want it to happen for each object we might collide with. Instead, we set it once, after we've determined if we collide with any objects.
This also helps show that you are setting gravity every frame you don't collide even though there doesn't seem to be any point at which you set it to zero; I suspect this is something you want to fix.

removing old tiles in a running game made with as3 and flash

I started to build a run game app for Android. I chose to make it in flash using tiles and Adobe Air. The game is that a player should run automatically to the right and avoid some obstacles by jumping or sliding along the ground.
I have made a function which always takes the first level in the array and uses as the starting level.
private function createLevel()
{
map_level.levell();
level = map_level.level1;
for(var t = 0; t < level.length; t++)
{
for(var u = 0; u < level[t].length; u++)
{
if(level[t][u] != 0)
{
var new_tile:platform_tile = new platform_tile;
addChild(new_tile);
new_tile.gotoAndStop(level[t][u]);
new_tile.x = u * 32;
new_tile.y = t * 32;
tiles.push(new_tile);
}
}
}
total_tile_width += u;
}
Then I create a function that takes a random level in the array of paths.
private function random_level ()
This level is then added at the end of the first track when the player has reached a certain length along the track, then the track seems endless and then made such that the camera follows the player.
private function update_level ()
{
random_level();
for(var t = 0; t < mid_lvl.length; t++)
{
for(u = 0; u < mid_lvl[t].length; u++)
{
if(mid_lvl[t][u] != 0)
{
var new_tile:platform_tile = new platform_tile;
level[t][u + total_tile_width] = mid_lvl[t][u];
addChild(new_tile);
new_tile.gotoAndStop(mid_lvl[t][u]);
new_tile.x = (u + total_tile_width) * 32;
new_tile.y = t * 32;
tiles.push(new_tile);
}
}
}
// Indstiller hvis spilleren skal have en stigende fart
if( movementspeed < 40)
{
movementspeed = movementspeed + 2;
}
else
movementspeed = movementspeed;
total_tile_width += u;
trace ("speed: " + movementspeed);
}
All this works as it should and game function also perfect as a PC, but the phone seems quick to overload it, since I can not figure out how to remove the old levels that have already been played and therefore there's going to be a lot levels in the phone memory.
I need to something like removeChild("old tiles the left the stage again) but got no idear how to only find the tiles that old and not just all tiles.
Anyone able to help me? btw hope you understand my question as im not the best at writing english.
Morten
You must use 2 BitmapData objects.
2 are the size of the screen. We call them screenBuffer1 and screenBuffer2
the other is larger than the screen by at least the width of a tile. We call it terrainBuffer
Then :
Init the terrainBuffer by drawing all visible tiles of the start screen
Copy the currently visible area on screenBuffer1
Draw sprites on screenBuffer1
Attach screenBuffer1 to stage
Now we start scrolling
Copy the currently visible area on screenBuffer2, offset by scroll amount
Draw sprites on screenBuffer2
Attach screenBuffer2 to stage
Continue increasing offset and alternate screenBuffer1 and screenBuffer2 so one is visible while you draw on the other
When offset reach the witdh of a tile :
if(offset>tileWidth){
offset-=tileWidth;
// Move content of terrainBuffer to the left by tileWidth pixels
// Draw a new column of tile on the right
}
Then keep on looping !

Ideas for jumping in 2D with Actionscript 3 [included attempt]

So, I'm working on the basics of Actionscript 3; making games and such.
I designed a little space where everything is based on location of boundaries, using pixel-by-pixel movement, etc.
So far, my guy can push a box around, and stops when running into the border, or when try to the push the box when it's against the border.
So, next, I wanted to make it so when I bumped into the other box, it shot forward; a small jump sideways.
I attempted to use this (foolishly) at first:
// When right and left borders collide.
if( (box1.x + box1.width/2) == (box2.x - box2.width/2) ) {
// Nine times through
for (var a:int = 1; a < 10; a++) {
// Adds 1, 2, 3, 4, 5, 4, 3, 2, 1.
if (a <= 5) {
box2.x += a; }
else {
box2.x += a - (a - 5)*2 } } }
Though, using this in the function I had for the movement (constantly checking for keys up, etc) does this all at once.
Where should I start going about a frame-by-frame movement like that? Further more, it's not actually frames in the scene, just in the movement.
This is a massive pile of garbage, I apologize, but any help would be appreciated.
try doing something like: (note ev.target is the box that you assigned the listener to)
var boxJumpDistance:Number = 0;
function jumpBox(ev:Event){
if (boxJumpDistance<= 5) {
ev.target.x += boxJumpDistance; }
else if(boxJumpDistance<=10){
ev.target.x += boxJumpDistance - (boxJumpDistance - 5)*2
}
else{
boxJumpDistance = 0;
ev.target.removeEventListener(Event.ENTER_FRAME, jumpBox);
}
}
then instead of running the loop, just add a listener:
box2.addEventListener(Event.ENTER_FRAME, jumpBox);
although this at the moment only works for a single box at a time (as it is only using one tracking variable for the speed), what you would really want to do is have that function internally to the box class, but im unsure how your structure goes. the other option would be to make an array for the boxes movement perhaps? loop through the array every frame. boxesMoveArray[1] >=5 for box 1, etc.