Constant Character Movement - AS3 - actionscript-3

How am I (am I?) suposed to make character movement with Box2D? I'll need the player to collide with the floor and others, however when it comes to moving him right or left, impulses aren't very handy. At least not what I have right now.
private function update(e:Event):void {
if (keys[Keyboard.RIGHT]) {
impulse_x = 3;
impulse_y = 0;
mainChar.ApplyImpulse(new b2Vec2(impulse_x, impulse_y), mainChar.GetPosition());
} else {
impulse_x = 0;
impulse_y = 0;
mainChar.ApplyImpulse(new b2Vec2(impulse_x, impulse_y), mainChar.GetPosition());
}
}
How do I move my character in a steady pace, as opposed to what the code above does which is accelerate mainChar every update when the right key is being pressed.
EDIT:
So there was this code there but it's on c++ I think - I'd appreciate a translation, since it's so small, to AS3 ^^ as I have no idea what float is doing, syntax is a bit confusing to me.
b2Vec2 vel = body->GetLinearVelocity();
float desiredVel = 0;
switch ( moveState )
{
case MS_LEFT: desiredVel = -5; break;
case MS_STOP: desiredVel = 0; break;
case MS_RIGHT: desiredVel = 5; break;
}
float velChange = desiredVel - vel.x;
float impulse = body->GetMass() * velChange; //disregard time factor
body->ApplyLinearImpulse( b2Vec2(impulse,0), body->GetWorldCenter() );
In AS3
switch (moveState)
{
case "RIGHT": desiredVel = 1.4; break;
case "LEFT": desiredVel = -1.4; break;
case "STOP": desiredVel = 0; break;
}
velChange = desiredVel - mainChar.GetLinearVelocity().x;
impulse.x = velChange;
impulse.y = 0.0;
mainChar.ApplyImpulse(impulse, mainChar.GetWorldCenter());
(Should I delete the initial part of the post and leave the answer?)

You should use a force. It's well described here: http://www.iforce2d.net/b2dtut/forces

Related

supplied DisplayObject must be a child of the caller fix

Im confused as to why my codes saying this,
My game creates a procedural generated map and the map is split into chuncks. The map works fine, i can switch areas and stuff no problem but when i delete a tree tile and re-add a grass tile then try to switch areas it tells me "The supplied DisplayObject must be a child of the caller." Iv gottn and fixed and slighty understand this problem, but i feel as if it IS a child of the calleer. idk :c
How my code is set up it creates a world class on my level class, then in that world a worldTiles sprite is created to place the tiles of the world into. This is where the tiles are originally added and deleted
This is where im pretty sure my problem is, the fucntion that deletes a tree tile and adds a grass tile
protected function mouseOnTile()
{
for (var i:int; i < world.tilesInWorld.length; i++)
{
if (mouse.hitTestObject(world.tilesInWorld[i]))
{
trace(world.tilesInWorld[i].Name);
if (world.tilesInWorld[i].Name == "tree")
{
var tx:int = world.tilesInWorld[i].x;
var ty:int = world.tilesInWorld[i].y;
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
world.tile = world.tilePool.getSprite();
world.tile.width = world.TILE_SIZE;
world.tile.height = world.TILE_SIZE;
world.tile.x = tx;
world.tile.y = ty;
world.tilesInWorld.push(world.tile);
world.worldTiles.addChild(world.tile);
}
}
}
}
Im sure it has something to do with how im re-adding the grass tile into the worldTiles, but im confused on how else i could do it?
This is the function that deletes the tiles when you walk to a different screen section
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = tilesInWorld.length - 1; i >= 0; i--)
{
worldTiles.removeChild(tilesInWorld[i]);
switch (tilesInWorld[i].Name)
{
case "water" :
waterPool.returnSprite(tilesInWorld[i]);
break;
case "shallow" :
shallowPool.returnSprite(tilesInWorld[i]);
break;
case "shell" :
shellPool.returnSprite(tilesInWorld[i]);
break;
case "sand" :
sandPool.returnSprite(tilesInWorld[i]);
break;
case "tree" :
treePool.returnSprite(tilesInWorld[i]);
break;
case "grass" :
tilePool.returnSprite(tilesInWorld[i]);
break;
case "rock" :
rockPool.returnSprite(tilesInWorld[i]);
break;
case "stone" :
stonePool.returnSprite(tilesInWorld[i]);
break;
}
}
tilesInWorld.length = 0;//empty array
generateTile();
}
}
This is where the tiles are generated onto the screen after being deleted
public function generateTile()
{
var Xcalc:int = (X + (800 / TILE_SIZE) / GlobalCode.MAP_SCALE);
var Ycalc:int = (Y + (600 / TILE_SIZE) / GlobalCode.MAP_SCALE);
for (var i:int = X; i < Xcalc; i++)
{
for (var j:int = Y; j < Ycalc; j++)
{
hm = heightmap[i][j];
if ((hm >= 0.84))
{
tile = waterPool.getSprite();
}
else if (((hm >= 0.8) && hm < 0.84))
{
tile = shallowPool.getSprite();
}
else if (((hm >= 0.79) && hm < 0.799))
{
tile = shellPool.getSprite();
}
else if (((hm >= 0.7) && hm < 0.8))
{
tile = sandPool.getSprite();
}
else if (((hm >= 0.35) && hm < 0.4))
{
tile = treePool.getSprite();
}
else if (((hm >= 0.2) && hm < 0.7))
{
tile = tilePool.getSprite();
}
else if (((hm >= 0.09) && hm < 0.2))
{
tile = stonePool.getSprite();
}
else
{
tile = rockPool.getSprite();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % 800);
tile.y = TILE_SIZE * (j % 600);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
I believe your problem is here:
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
...
}
You are lopping forward through the array, using removeChild on an element in the array, and using "pop" which removes the last item of the array, not the item that was actually removed. You eventually will hit an item that was already removed. Additionally, your i pointer changes each time you pop, which means the loop will never hit every item and is fundamentally flawed.
The DisplayObject used as the argument in dO.removeChild() must be defined, non-null, and a child of dO (i.e. added with dO.addChild(). If it does not meet all of those requirements, it will error out.
To fix this, use splice() instead of pop() (which will allow you to remove a specific element in an array) and go backwards through the array (which will handle the i pointer issues)
for (var i:int = world.tilesInWorld.length - 1; i >= 0; --i)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
...
}
You can also loop forward through the array, but you need to modify the pointer. This is slower and more error prone than going backwards, but can work just as well (when I say slower, we're talking microseconds difference unless you are doing massive computations).
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
--i;
...
}
Additionally, and this is just a syntax/readability thing, you should never rely on a datatype's default value. int will default to 0, but you should still declare it as var i:int = 0 to make it easy to change in the future, standardized and easy to read, and so that you could easily change it to a Number, which has a much, much higher max value than int but defaults to NaN.
This does not make any sense. Since you are removing all element from the array you can jusr forget about pop or slice and do
world.tilesInWorld.length = 0;
after the loop.
Since you are removing all object from world.worldTiles don't bother removing them one by one and do:
world.worldTiles.removeChildren()
after the loop.
Finally you are left with a simple loop where you only do:
world.treePool.returnSprite(world.tilesInWorld[i]);
It's a case (in your case and in the answer given) where you both try very hard to make a simple code as complicated as possible.

Moving objects on the stage

Let's say I have 5 objects in an array and I move all them along x-axis like this:
vx = 5;
for (i:int = 0; i < objects.length; i++)
{
objects[i].x += vx;
}
I would like to make this.
If any object from array 'objects' hit PointA, move all objects from that array to left side, for example set vx *= -1;
I can make only this:
for (i:int = 0; i < objects.length; i++)
{
// move right
objects[i].x += vx;
if (objects[i].hitTest(PointA))
{
// move left
vx *= -1;
}
}
This will change object's direction, but I need to wait all objets hit PointA.
How to change the direction of all objects in array, if any of them hit PointA?
I don't know action script but you should set a boolean outside the for loop, like bGoingRight
Check that this is true and move objects right, if its false move the objects left. When you pass the hitTest you should changed the boolean to false.
Rough Example
var bGoRight:Boolean = true;
for (i:int = 0; i < objects.length; i++)
{
if(bGoRight)
{
// move right
objects[i].x += vx;
}
else
{
// move left
vx *= -1;
}
if (objects[i].hitTest(PointA))
{
// if any object hit the point, set the flag to move left
bGoRight = false;
}
}
So you'll need to check the objects that have already hit PointA, store them, then check if the updated storage count is equivalent to your objects array. Then when that case is satisfied you can change the vx variable. This could look something like this:
//set a variable for storing collided object references
//note: if you are doing this in the IDE remove the 'private' attribute
private var _contactList:Array = [];
for (i:int = 0; i < objects.length; i++)
{
// move right
objects[i].x += vx;
if (objects[i].hitTest(PointA))
{
if( contactList.length == objects.length ) {
// move left
vx *= -1;
//clear our contact list
contactList.length = 0;
}
else if ( noContact( objects[i] ) ) {
contactList.push( objects[i] );
}
}
}
Then the function in the else if statement noContact(), again if you are doing this in the IDE you will need to remove the private attribute.
private function noContact( obj:* ):Boolean {
if ( contactList.indexOf( obj ) != -1 ) {
return false;
}
return true;
}
Another way you can do this is like this (a boolean way as stated in the other answer), but is reliant on your storage setup correct:
//set this variable for directionRight (boolean)
private var directionRight:Boolean = true;
for (i:int = 0; i < objects.length; i++)
{
// move right
objects[i].x += vx;
if (objects[i].hitTest(PointA))
{
//we are moving to the right, object[i] is the last object
if( i == objects.length-1 && directionRight ) {
// move left
directionRight = !directionRight;
vx *= -1;
}
else if ( i == 0 && !directionRight ) ) {
// move right
directionRight = !directionRight;
vx *= -1;
}
}
}

How to call AS3 Switch

I am struggling a bit with my switch statement (I've never used switch before). I have a hit test for when my object reaches the top or bottom of the stage. When this happens, I want to switch states (the game in question is a simple platformer that allows the player to switch the gravity when they hit a new surface. Below is my current code:
{
...
if(player.hitTestObject(bottom)) {
//Switch state to normal
}
if(player.hitTestObject(top)) {
//Switch state to inverted
}
}
switch (myGrav){
case "NORMAL":
trace("Normal")
player.gotoAndPlay(1);
oktoJump = false;
player.y = 376.5;
case "INVERTED":
trace("Inverted")
player.gotoAndPlay(8);
oktoJump = false;
player.y = 12;
}
Thanks!
Cases within your switch statement are missing a break; therefore, code will continue to execute through the switch statement.
This should be:
var myGrav:String = "NORMAL";
if (player.hitTestObject(bottom))
myGrav = "NORMAL";
if (player.hitTestObject(top))
myGrav = "INVERTED";
switch (myGrav)
{
case "NORMAL":
trace("Normal")
player.gotoAndPlay(1);
oktoJump = false;
player.y = 376.5;
break;
case "INVERTED":
trace("Inverted")
player.gotoAndPlay(8);
oktoJump = false;
player.y = 12;
break;
}
I prefer less variables when I can get away with it.
switch (true){
case (player.hitTestObject(bottom)):
trace("Normal")
player.gotoAndPlay(1);
oktoJump = false;
player.y = 376.5;
break;
case (player.hitTestObject(top)):
trace("Inverted")
player.gotoAndPlay(8);
oktoJump = false;
player.y = 12;
break;
}

Dual fire and positioning problems

I'm working on a space shooter and have this problem:
My space ship fires bullets. The bullets are centered in reference to the ship. Currently all bullets are single shots.
Now what I'd like to have is dual fire for the ship. Basically that means I have to double the shots and place them at the left and right
side of the ship, right? But I don't know how to do it exactly.
The other problem I have is that when I change the x and y-coordinates of the bullet in order to move the bullets from the center of the ship to the left (or right) and rotate the ship and fire I get a strange behavior of the bullets. In this case they move from the left to the center and vice versa referring to the ship (while rotating and firing).
Please - has anyone any idea where the problems might be and how to do it? I appreciate very much any help. TIA!:)
private function autoShoot(step:Number):void {
projectileManager.projectilePoolCount = projectileManager.projectilePool.length - 1;
asteroidManager.asteroidCount = asteroidManager.asteroids.length;
if (projectileManager.lastProjectileShot > projectileOffset && projectileManager.projectilePoolCount > 0 &&
playerStarted && asteroidManager.asteroidCount > 0 && projNumber < projectileNumber)
{
dispatchEvent(new CustomEventSound(CustomEventSound.PLAY_SOUND, Main.SOUND_PLAYER_SHOOT,
false, 0, 8, 1));
tempProjectile = projectileManager.projectilePool.pop();
tempProjectile.projectileDelayCount = 0;
var projectileRadians:Number = (player.frame / 360) * 6.28;
projShots++;
projNumber++;
if (projNumber >= projectileNumber)
{
player.startDelay = true;
}
tempProjectile.x = (player.point.x) + Math.cos(projectileRadians);
tempProjectile.y = (player.point.y) + Math.sin(projectileRadians);
tempProjectile.x = player.x + 13;
tempProjectile.y = player.y + 13;
tempProjectile.nextX = tempProjectile.x;
tempProjectile.nextY = tempProjectile.y;
tempProjectile.dx = rotationVectorList[player.frame].x;
tempProjectile.dy = rotationVectorList[player.frame].y;
tempProjectile.speed = projectileSpeed;
tempProjectile.frame = 0;
tempProjectile.bitmapData = tempProjectile.animationList[0];
tempProjectile.projectileDelay = 10;
projectileManager.projectiles.push(tempProjectile);
projectileManager.lastProjectileShot = 0;
} else {
projectileManager.lastProjectileShot += step;
}
}
The autoShoot function only fires one projectile each time it is called. There is only one tempProjectile per call to push to the projectiles list.
So one fix can be to add a tempProjectile2 (in the same place tempProjectile is defined) as a variable that can be used. Now in each call the projectile pool gets depleted by two, as long an adequate number of projectiles are popped
tempProjectile = projectileManager.projectilePool.pop();
tempProjectile2 = projectileManager.projectilePool.pop();
then adjust the offset accordingly
tempProjectile.x
tempProjectile.y
tempProjectile2.x
tempProjectile2.y
And so on with the rest of the code. The only thing I am unclear about is why tempProjectile.x and y are assigned twice.
tempProjectile.x = (player.point.x) + Math.cos(projectileRadians);
tempProjectile.y = (player.point.y) + Math.sin(projectileRadians);
tempProjectile.x = player.x + 13;
tempProjectile.y = player.y + 13;
only the second pair will be used, I think.
Hopefully the rest of the functions should not change because at the end the projectiles are pushed to the projectiles list
projectileManager.projectiles.push(tempProjectile);
projectileManager.projectiles.push(tempProjectile2);
So the whatever function used to update and render should remain the same, assuming tempProjectile is not tied down elsewhere in the program.

ActionScript 3 - Walls don't collide properly

For several weeks I'm been trying to make my topdown game. It went well for some time, but then at some point I wanted to create a scrolling map with walls everywhere. Now, to make it easy to create the map (and add more later) I made a class called "Wall" which I will hit test. This works, when it hits, the map must stop scrolling. It does, so good so far.
Now, when the player moves away from the object, I want the map to be able to scroll again, this works too, but now the player can't move to the side the player came from. I know this is because I need to define the sides, where the player enters, in order tell the game which movement must be set to zero at that point.
You can see the code here:
public function AddWalls(player:MovieClip)
{
WallObjects = new Array();
for (var i:int = 0; i < this.numChildren; i++)
{
var mc = this.getChildAt(i);
if (mc is Wall)
{
var wallobj:Object = new Object();
wallobj.mc = mc;
wallobj.leftside = mc.x;
wallobj.rightside = mc.x + mc.width;
wallobj.topside = mc.y;
wallobj.bottomside = mc.y + mc.height;
wallobj.width = mc.width;
wallobj.height = mc.height;
WallObjects.push(wallobj);
}
}
}
public function EnableCollisionWithWalls():void
{
for (var k:int = 0; k < WallObjects.length; k++)
{
//if (player.y > WallObjects[k].topside && player.y < WallObjects[k].bottomside && player.x > WallObjects[k].leftside && player.x < WallObjects[k].rightside)
if (player.hitTestObject(WallObjects[k].mc))
{
if (player.x > WallObjects[k].leftside && player.x < WallObjects[k].leftside+15)
{
Lefthit = true;
trace(DebugVar);
DebugVar++;
player.x = WallObjects[k].leftside;
Scroll_x = 0;
}
else
if ( player.x < WallObjects[k].leftside -1 || (player.y > WallObjects[k].leftside ))
{
Lefthit = false;
}
if (player.hitTestObject(derp))
{
Lefthit = false;
}
}
}
}
public function EnableMovement():void
{
map.x += Scroll_x;
map.y += Scroll_y;
for (var i:int = 0; i < this.numChildren; i++)
{
var mc = this.getChildAt(i);
if (mc is Wall)
{
mc.x += Scroll_x;
mc.y += Scroll_y;
}
}
}
public function MovementKeysDown(move:KeyboardEvent):void
{
var Speed:int = -5;
switch (move.keyCode)
{
case 37: // venstre knap
Scroll_x = -Speed;
break;
case 38: // op
Scroll_y = -Speed;
break;
case 39: // højre knap
Scroll_x = Speed;
if (Lefthit)
{
Scroll_x = 0;
}
break;
case 40: // ned
Scroll_y = Speed;
break;
default:
}
}
public function MovementKeysUp(move:KeyboardEvent):void
{
switch (move.keyCode)
{
case 37:
Scroll_x = 0;
break;
case 38:
Scroll_y = 0;
break;
case 39:
Scroll_x = 0;
break;
case 40:
Scroll_y = 0;
break;
default:
}
}
Might be some syntax errors (since I removed some code in this editor).
You can see the current version here.
In this version the scroll keeps on going. I did come up with a "fix" for it, by check if the player was 1 pixel away from the movieclip, inside the hit test (which for some reason works, which I guess it shouldn't since it doesn't hit anymore) and then setting the Lefthit to false. However this is not a good solution and if you continue up or down away from the movieclip, you are still not able to go right anymore...
I've been baffled by this for a long time, so I thought it was about time I asked for help. I couldn't find anything on how to control movement in a top-down game, with a scrolling map + wall :/
The simplest (but not most resource friendly) solution (if you anyway have a single storage for walls) is iterating through the walls and instead of using the Flash default hitTest (I don't like the way it works since ActionScript 2) - just check the coordinates and if you see that there's going to be a collision on the next simulation step - handle it according to the game logic.
The most useful optimization for this algorithm is creating a filter/data structure for getting only walls that are near to the player and so can be affected to the test for collisions.