I'm trying to make the player body, when certain condition is met, to go up few "meters" then stop slowly in the "air" and come down.
The gravity is 600
public var gravity:Number = 600;
public var space:Space = new Space(new Vec2(0, gravity));
I thought that for it to go up, the impulse.y should be at least -601, to contradict the force that pushes it down. Although, setting -600 (or even smaller) will shoot it upwards never to be seen again. (even -100).
Yes the impulse is in the update function which means it will constantly add the impulse making it faster per tick. However, when setting -20 it doesn't go up in the y, (it should eventually as impulse is being given to the body per tick).
switch (combo)
{
case "W":
break;
case "WW":
impulse.x = 0.0;
impulse.y = -powerJump;
mainChar.applyImpulse(impulse);
powerJump -= 5;
break;
}
This is not finished and it is not what is described above but it's the gist of it. When condition is met the player should go upwards and the impulse should lose it's force. The problem is I don't know how much is sufficient to counter the gravity impulse. I can do it by trial'n error but I'd rather know how it works, how much impulse is equivalent to gravity=600?
Related
I have a function where I can control my character.
I also have a movieclip on the stage called assassin_table.
I want to make it so that the character can't move in the table, a.k.a make the table work like a wall.
I have this code:
if(!this.hitTestObject(_root.assassin_table))
{
if(upKeyDown)
{
gotoAndStop(4);
y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
x-=Math.sin(rotation/-180*Math.PI)*(mainSpeed +7);
}
if(!upKeyDown)
{
gotoAndStop(3);
}
}
However, if I touch the table, then I can't move at all.
I know it's because if(!this.hitTestObject(_root.assassin_table)), but I don't understand the logic behind NOT moving through objects. I'd much rather have a near pixel-perfect collision detection system, but since it's so hard to find any good info online which isn't confusing, I'll stick with hitTestObject for now :)
EDIT: Tried something, didn't really work that well.
if(!_root.assassinDead && !teleporting && this.currentFrame != 5)
{
if(this.hitbox.hitTestObject(_root.assassin_table))
{
_root.assassin_table.gotoAndStop(2);
if(this.x > _root.assassin_table.x)
{
trace("Can't move right");
canMoveRight = false;
}
if(this.x <_root.assassin_table.x)
{
trace("Can't move left");
canMoveLeft = false;
}
if(this.y > _root.assassin_table.y)
{
trace("Can't move up");
canMoveUp = false;
}
if(this.y < _root.assassin_table.y)
{
trace("Can't move down");
canMoveDown = false;
}
}
else
{
canMoveRight = true;
canMoveLeft = true;
canMoveUp = true;
canMoveDown = true;
}
}
This causes me to sometimes be able to walk through the table. I figure it's because my character can move in essentially every possible angle (since he's always facing the mouse and there are no tiles/grids).
How would I make it so that it would work with the advanced movement I have?
Moving up runs this:
y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
x-=Math.sin(rotation/-180*Math.PI)*(mainSpeed +7);
And the rotation is decided by this:
this.rotation = Math.atan2((stage.mouseY - this.y), (stage.mouseX - this.x)) * 180/ Math.PI + 90;
You should seperate your hittest functions for four different moving directions.
I mean, you shouldn't use this "hitTestObject" stuff, that only returns a boolean value "true" or "false", and that's not going to work for you.
You need a function like "testPoint(player.x, player.y);" and returns the object at the given position, so you can implement it for your game like that
if (upKeyDown && testPoint(player.x, player.y - mainSpeed +7) == null) y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
player.y - mainSpeed +7 // that checks for plus mainSpeed+7 because of trying to stop your player before it get stack inside object
Basically your logic flow should be like this:
Sample input (key press)
Move character
Check for collisions
If collision then move character to it's "outside" the object that it's touching
In your particular case, if your character is on the left of the table, and you're moving right, then first things first, you move your character. At your new position, check for any collisions. If you have a collection, then because you were moving from the left, we want to be on the left of the object that we colliding with, in this case the table, so we position our character to the left of the table.
The first part of that (checking if the character has hit the table) is called collision detection. Moving the character so that it's outside the bounds of the table is called collision response. Collision detection is a pretty big field and really depends on the type of game you're making; you can do grid-based, hit-test based (if you don't have a ton of objects), physics-based, math-based etc. Collision response can be anything and everything, depending on how you want to react to a collision - you can destroy the object (balloon on a spike), change its speed (character running through mud), bounce off it (ball off wall), or stop any further movement (character against wall).
To make things a bit easier:
Separate your systems - your input shouldn't be dependant on your collision for example. If the up key is down, just register that fact - what you do with it later (make your character move) is up to you
Separate your objects position in memory from its position on screen - this will let you move it around, react to collisions etc, and only when everything is good, update the graphics (stops things like graphics entering a wall only to jump out the next frame)
Solve for one axis at a time - e.g. collide on the x axis first, then the y
Pixel perfect collision is rarely needed :) Non-rotated boxes and circles will work a lot more than you'd think
Somewhat related - the shape of your object doesn't have to be the shape that you're colliding with - e.g. your table collision shape could just be a box; your character collision shape could just be a circle
Update
This causes me to sometimes be able to walk through the table
Assuming that we're going to collide our character and table as boxes, you need to take into account their sizes - i.e. we don't just compare the x values, but the right side of our character box against the left side of the table box etc. Something like:
// assumes objects have their anchor point in the top left - if its
// not the case, adjust as you see fit
if( char.x + char.width > table.x ) // right side of the char is overlapping the left side of the table
canMoveRight = false;
else if( char.x < table.x + table.width ) // left side of char vs right side of table
canMoveLeft = false;
// etc
When working with collisions, it's always nice to see the actual boxes, for debugging. Something like this:
var debug:Shape = new Shape;
debug.addEventListener( Event.ENTER_FRAME, function():void
{
debug.graphics.clear();
debug.graphics.lineStyle( 2.0, 0xff0000 );
// char (again assuming anchor point is top left)
debug.graphics.drawRect( char.x, char.y, char.width, char.height );
// table
debug.graphics.drawRect( table.x, table.y, table.width, table.height );
});
// add our debug. NOTE: make sure it's above everything
stage.addChild( debug );
Okay Im relatively new to nape and Im in the process of making a game, I've made a Body called platform of type KINEMATIC, and I simply want to move it back a forth in a certain range on the stage. Can somebody please see where im going wrong , thanks.
private function enterFrameHandler(ev:Event):void
{
if (movingPlatform.position.x <= 150 )
{
movingPlatform.position.x += 10;
}
if (movingPlatform.position.x >= 260)
{
movingPlatform.velocity.x -= 10;
}
}
First of in one of the if blocks you are incrementing position.x by 10 in the other one you are decrementing velocity.x by 10. I guess you meant position.x in both.
Secondly, imagine movingPlatform.position.x is 150 and your enterFrameHandler runs once. movingPlatform.position.x will become 160 and on the next time enterFrameHandler is called none of the if blocks will execute since 160 is neither less than or equal to 150 or greater than or equal to 260.
You can use the velocity to indicate the side its moving and invert it once you go beyond an edge, something like :
// assuming velocity is (1,0)
private function enterFrameHandler(ev:Event):void {
if (movingPlatform.position.x <= 150 || movingPlatform.position.x >= 260) {
movingPlatform.velocity.x = -movingPlatform.velocity.x;
}
movingPlatform.position.x += movingPlatform.velocity.x;
}
Obviously this might cause problems if the object is already at let's say x=100, it will just keep inverting it's velocity, so either make sure you place it between 150-260 or add additional checks to prevent it from inverting it's direction more than once.
This might be a better way of doing it :
// assuming velocity is (1,0)
private function enterFrameHandler(ev:Event):void {
if (movingPlatform.position.x <= 150) {
movingPlatform.velocity.x = 1;
} else if (movingPlatform.position.x >= 260) {
movingPlatform.velocity.x = -1;
}
movingPlatform.position.x += movingPlatform.velocity.x;
}
In general:
Kinematic bodies are supposed to be moved solely with velocity, if you change their position directly then they are not really moving as much as they are 'teleporting' and as far as the physics is concerned their velocity is still exactly 0 so things like collisions and friction will not work as you might expect.
If you want to still work with positions instead of velocities, then there's the method setVelocityFromTarget on the Body class which is designed for kinematics:
body.setVelocityFromTarget(targetPosition, targetRotation, deltaTime);
where deltaTime is the time step you're about to use in the following call to space.step();
All this is really doing is setting an appropriate velocity and angularVel based on the current position/rotation, the target position/rotation and the amount of time it should take to get there.
When one of my sprites is being dragged (moved around), I'm cycling through other sprites on the canvas, checking whether they are in range, and if they are, I set a background glow on them. Here is how I'm doing it now:
//Sprite is made somewhere else
public var circle:Sprite;
//Array of 25 sprites
public var sprites:Array;
public function init():void {
circle.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
}
private function startDrag(event:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, glowNearbySprites);
stage.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
circle.startDrag();
}
private function stopDrag(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, glowNearbySprites);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);
circle.stopDrag();
}
private function glowNearbySprites(event:MouseEvent):void {
for (var i = 0; i < sprites.length; i++) {
var tSprite = sprites.getItemAt(i) as Sprite;
if (Math.abs(tSprite.x - circle.x) < 30 &&
Math.abs(tSprite.y - circle.y) < 30) {
tSprite.filters = [new GlowFilter(0xFFFFFF)];
}
else {
tSprite.filters = null;
}
}
}
Basically I'm cycling through each sprite every time a MOUSE_MOVE event is triggered. This works fine, but the lag when dragging the sprite around is pretty noticeable. Is there a way to do this that is more efficient, with no or less lag?
Well, depending on the size of the amount of sprites you have, it may be trivial. However, if you're dealing with over 1k sprites -- use a data structure to help you reduce the amount of checks. Look at this QuadTree Demo
Basically you have to create indexes for all the sprites, so that you're not checking against ALL of them. Since your threshold is 30, when a sprite moves, you could place it into a row/column index of int(x / 30), int(y / 30). Then you can check just the sprites that exist in 9 columns around the row/column index of the mouse position.
While this would seem more cumbersome, it actually it way more efficient if you have more items -- the number of checks stays consistent even as you add more sprites. With this method I'm assuming you could run 10k sprites without any hiccup.
Other performance optimizations would be:
use an vector/array of sprites rather than getChildAt
preincrement i (++i)
store a static single instance glowfilter, so it's only one array, rather creating a separate filter for all the sprites.
GlowFilter is pretty CPU intensive. Might make sense to draw all the sprites together in one shot, and then apply GlowFilter once to it -- (this of course depends on how you have things set up -- might even be more cumbersome to blit your own bitmap).
Make your variable declaration var sprite:Sprite = .... If you're not hard typing it, it has to do the "filters" variable lookup by string, and not by the much faster getlex opcode.
I'd incorporate all the improvements that The_asMan suggested. Additionally, this line:
tSprite.filters = [new GlowFilter(0xFFFFFF)];
is probably really bad, since you're just creating the same GlowFilter over and over again, and creating new objects is always expensive (and you're doing this in a for loop every time a mouse_move fires!). Instead create it once when you create this class and assign it to a variable:
var whiteGlow:GlowFilter = new GlowFilter(0xFFFFFF);
...
tSprite.filters = [whiteGlow];
If you're still having performance issues after this, consider only checking half (or even less) of the objects every time you call glowNearbySprites (set some type of flag that will let it know where to continue on the next call (first half of array or second half). You probably won't notice any difference visually, and you should be able to almost double performance.
Attempting to compile the suggestions by others into a solution based on your original code, so far I've created the GlowFilter only once and re-used, secondly I've changed the loop to use a for each instead of the iterant based loop, third I've updated to use ENTER_FRAME event instead of MOUSE_MOVE. The only thing I've left out that's been suggested so far that I see is using a Vector, my knowledge there is pretty much nil so I'm not going to suggest it or attempt until I do some self education. Another Edit
Just changed the declaration of sprites to type Vector no code here for how it's populated but article below says you can basically treat like an Array as it has all the same method implemented but has a couple of caveats you should be aware of, namely that you cannot have empty spots in a Vector and so if that is a possibility you have to declare it with a size. Given it knows the type of the object this probably gets a performance gain from being able to compute the exact position of any element in the array in constant time (sizeOfObject*index + baseOffset = offset of item). The exact performance implications aren't entirely clear but it would seem this will always result in at least as good as Array times if not better.
http://www.mikechambers.com/blog/2008/08/19/using-vectors-in-actionscript-3-and-flash-player-10/
//Array of 25 sprites
public var sprites:Vector.<Sprite>;
private var theGlowFilterArray:Array;
public function init():void
{
theGlowFilterArray = [new GlowFilter(0xFFFFFF)];
circle.addEventListener(MouseEvent.MOUSE_DOWN, startDrag);
}
private function startDrag(event:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
addEventListener(Event.ENTER_FRAME, glowNearbySprites);
circle.startDrag();
}
private function stopDrag(event:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);
removeEventListener(Event.ENTER_FRAME, glowNearbySprites);
circle.stopDrag();
}
private function glowNearbySprites(event:Event):void
{
var circleX:Number = circle.x;
var circleY:Number = circle.y;
for each(var tSprite:Sprite in sprites) {
if (Math.abs(tSprite.x - circleX) < 30 && Math.abs(tSprite.y - circleY) < 30)
tSprite.filters = theGlowFilterArray;
else
tSprite.filters = null;
}
}
You problem is that making calculations that are at least linear O(n) on every mouse change event is terribly inefficient.
One simple heuristic to bring down the amount of times that you make your calculations is to save the distance to the closest sprite and only after mouse moved that distance would you recalculate the potential crash. This can be calculated in constant time O(1).
Notice that this works only when one sprite moves at a time.
I move a Sprite across the screen using the keyboard, at a rate of 10 pixels/ENTER_FRAME event fire. The issue is that, when it moves, you can see it being "redrawn" every 10 pixels, which makes it hard to look at. I haven't seen this in other Flash games.
If you look closely, you can also see this here (although at a much lower scale): http://kirill-poletaev.blogspot.com/2010/07/smooth-character-movement-using-as3.html
I want to get rid of that effect, any ideas?
If the player is at a distance of ... from the screen edge, it stops moving (by moving in the opposite direction), and the BG starts scrolling (the same visual effect can be seen).
Music in playing in the background, a minimap is updated with the player's position.
private function updater(e:Event):void
{
if(up && GlobalVars.vars.upPossible)
{
cont.y-=unit;
setu(); // Player graphics state
}
else if(down && GlobalVars.vars.downPossible)
{
cont.y+=unit;
setd(); // Player graphics state
}
else if(left && GlobalVars.vars.leftPossible)
{
cont.x-=unit;
setl(); // Player graphics state
}
else if(right && GlobalVars.vars.rightPossible)
{
cont.x+=unit;
setr(); // Player graphics state
}
else
{
ups.visible=false; downs.visible=false; rights.visible=false;
lefts.visible=false; normals.visible=true; // Player graphics state
GlobalVars.vars.scXr=0; GlobalVars.vars.scYu=0; GlobalVars.vars.scXl=0;
GlobalVars.vars.scYd=0; cont.x=int(cont.x); cont.y=int(cont.y); //Someone from the Kongregate.com forums suggested this, no visible effect
}
if((cont.x=GlobalVars.vars.maxX))
{
if(cont.x=GlobalVars.vars.maxX && right && GlobalVars.vars.canScrollR) GlobalVars.vars.scXr=1, cont.x-=unit, setr();
}
else GlobalVars.vars.scXl=0, GlobalVars.vars.scXr=0; //BG Scrolling
if((cont.y=stage.stageHeight*7.3/10))
{
if(cont.y=stage.stageHeight*7.3/10 && down && GlobalVars.vars.canScrollD) GlobalVars.vars.scYd=1, cont.y-=unit, setd();
}
else GlobalVars.vars.scYu=0, GlobalVars.vars.scYd=0; //BG Scrolling
if(cont.y>=stage.stageHeight*7.3/10 && cont.x>=GlobalVars.vars.maxX) GlobalVars.vars.minimapTr=1;
else GlobalVars.vars.minimapTr=0;
if(cont.y-unitGlobalVars.vars.sH-cont.height-3.1) GlobalVars.vars.downPossible=false;
else GlobalVars.vars.downPossible=true;
if(cont.x-unitGlobalVars.vars.sW-cont.width-3.5) GlobalVars.vars.rightPossible=false;
else GlobalVars.vars.rightPossible=true;
GlobalVars.vars.plX=cont.x; //for minimap
GlobalVars.vars.plY=cont.y;
Also, key listener functions:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyD, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_UP, keyU, false, 0, true);
private function keyD(e:KeyboardEvent):void
{
if(e.keyCode==37 || e.keyCode==65) left=true;
if(e.keyCode==38 || e.keyCode==87) up=true;
if(e.keyCode==39 || e.keyCode==68) right=true;
if(e.keyCode==40 || e.keyCode==83) down=true;
}
private function keyU(e:KeyboardEvent):void
{
if(e.keyCode==37 || e.keyCode==65) left=false;
if(e.keyCode==38 || e.keyCode==87) up=false;
if(e.keyCode==39 || e.keyCode==68) right=false;
if(e.keyCode==40 || e.keyCode==83) down=false;
}
I've encountered some improvement by increasing the FPS to 120, and decreasing the step to 4, but it's still there. I'm fairly sure it's not a performance issue, but, rather, a movement method fault.
A couple of suggestions:
Increase the frame rate
Use a tween library (e.g. GTween) with some easing effect.
Basically, if you want the object to jump 10px to the right, don't just move it right away, let it animate to its new position with some easing effect. Additionally, if the object is still moving and the key is pressed again, you probably want to accelerate the movement a bit (but only up to a point!).
I think you're asking how to make it more smooth. Well:
If you change the movement 2px per frame, then you'll have a much smoother experience. However, the movement will see very slow. To combat this, you just increase the frame rate of your swf. I believe the maximum is 120 frames, but that should be more than enough.
I love Tweenlite, it's great for pre-defined effects. It isn't for the core of your game though so I'd use Tweenlite for UI effects but barely for ingame movements responding to user controls.
I think around 24-30 fps for games is great, that's how I set all my games. I think abit you're picky, but perhaps what you're missing is a bit of edge: a smooth start and end for the movement. In most platformers I have a similar mechanic:
You want the start of the movement to start slow, reach the normal (max) speed you define and when the player lets go of a button, it slows down back to 0. This means you need to check the speed on enter frame regardless of the button triggers (they increment the speed but aren't the only condition to change speed and move the object)
enterframe loop:
//check for each direction:
if up, y_speed++
else if down, y_speed--
else if right, x_speed++
else if left, x_speed--
else // perform a decrease:
y_speed *= .8
x_speed *= .8
// make sure we aren't past the max speed
if x_speed>max_x_speed
x_speed = max_x_speed
etc. else other direction and y_speed
// now apply the movement
object.x += x_speed
object.y += y_speed
// remove the enterframe for better performance
if math.abs(x_speed)<.1 && math.abs(y_speed)<.1
// remove enterframe for movement here, add it again next time we know we have movement (button downs related to the movement, etc.)
I would implement this in your code but your code is too crowded. Anyway otherwise, most games with these simple controls are working just like your application does.
when tracing the mouseX / mouseY or localX / localY coordinates of a display object, why does x start at 1 while y starts at 0?
for example, i've drawn a simple 400 x 400 pixel sprite onto the stage with a MouseEvent.MOUSE_MOVE event listener that calls a handler function to trace the local coordinates of the mouse.
the first, top-left pixel returns {x:1, y:0} and the last, bottom-right pixel returns {x:400, y:399}. shouldn't both the x and y start and end with the same value? i'm not sure which makes more sense for a the first mouse coordinate (either 0 or 1) but it certainly doesn't make sense that they are different?
[SWF(width = "1000", height = "600", backgroundColor = "0xCCCCCC")]
import flash.display.Sprite;
import flash.events.MouseEvent;
var darkBlueRect:Sprite = createSprite();
darkBlueRect.x = 23;
darkBlueRect.y = 42;
addChild(darkBlueRect);
darkBlueRect.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
function mouseMoveEventHandler(evt:MouseEvent):void
{
trace(darkBlueRect.mouseX, evt.localX, darkBlueRect.mouseY, evt.localY);
}
function createSprite():Sprite
{
var result:Sprite = new Sprite();
result.graphics.beginFill(0x0000FF, 0.5);
result.graphics.drawRect(0, 0, 400, 400);
result.graphics.endFill();
return result;
}
Report a bug, you've found one:
http://bugs.adobe.com/flashplayer/
I thought perhaps it was to make room for a line applied to the sprite among other things but all those tests proved not to be the case. This is, in my opinion, a bug. File it and I'll second it, or if you can't be bothered let me know I'll file it cause it should be fixed.
Update
I've just tried another test where I set the stageScaleMode to allow for scaling, then zoomed in 2-3 times to the object and tried to get a 0/0 reading on MOUSE_DOWN. Just can't be done apparently. I did get a 0 reading on the X finally but then the Y is out. I think this issue may come down to the fact that flash returns X/Y pointer position as a Number and not an int, and you can get decimal values on the pointer position as you'll notice if you're zoomed in. Perhaps it's just buggy code or it comes down to floating point precision issues based on how the decimal points are calculated, or if you're not zoomed in and the object isn't scaled, flash rounds that decimal value off an int and this may also explain the weird behavior. Dunno just guessing, thought I'd add the results of this extra test.