I have a function here for moving a Sprite on every key pressed. Now I also want to move it on key hold instead of pressing the key repeatedly but I have no idea how to do it. Please guide me, your help is very much appreciated.
keyBoardListener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event)
{
Vec2 location = event->getCurrentTarget()->getPosition();
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
case EventKeyboard::KeyCode::KEY_A:
event->getCurrentTarget()->setPosition(location.x - 10.0f, location.y);
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
case EventKeyboard::KeyCode::KEY_D:
event->getCurrentTarget()->setPosition(location.x + 10.0f, location.y);
break;
case EventKeyboard::KeyCode::KEY_UP_ARROW:
case EventKeyboard::KeyCode::KEY_W:
event->getCurrentTarget()->setPosition(location.x, location.y + 10.0f);
break;
case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
case EventKeyboard::KeyCode::KEY_S:
event->getCurrentTarget()->setPosition(location.x, location.y - 10.0f);
break;
}
};
There's no event that triggers continuously when a key is pressed so one way to solve your problem is to have a global(or class or whatever) variable that tracks the movement on the x axis and one for the y axis.
To use only two variables and not a separate one for each key you could use 2 integers, let's say xMovement and yMovement and set their values to -1, 0 or 1, based on what keys are pressed. If xMovement is -1, move your sprite to the left, if it's 1 move it to the right and if it's 0 don't move it at all. Same thing for the y axis. To achieve this you should change your code like this:
keyBoardListener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event)
{
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
case EventKeyboard::KeyCode::KEY_A:
xMovement--;
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
case EventKeyboard::KeyCode::KEY_D:
xMovement++;
break;
case EventKeyboard::KeyCode::KEY_UP_ARROW:
case EventKeyboard::KeyCode::KEY_W:
yMovement++;
break;
case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
case EventKeyboard::KeyCode::KEY_S:
yMovement--;
break;
}
};
Now you should also add a key release event, where the incrementation/decrementation should be the opposite from the key pressed event:
keyBoardListener->onKeyReleased = [](EventKeyboard::KeyCode keyCode, Event* event)
{
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
case EventKeyboard::KeyCode::KEY_A:
xMovement++;
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
case EventKeyboard::KeyCode::KEY_D:
xMovement--;
break;
case EventKeyboard::KeyCode::KEY_UP_ARROW:
case EventKeyboard::KeyCode::KEY_W:
yMovement--;
break;
case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
case EventKeyboard::KeyCode::KEY_S:
yMovement++;
break;
}
};
And now, to actually move you sprite, just do something like this in an update function that gets called every frame:
float newPosX = sprite->getPositionX() + (xMovement * 10.f);
float newPosY = sprite->getPositionY() + (yMovement * 10.f);
sprite->setPosition(newPosX, newPosY);
You'll also need some mechanism to ensure that xMovement and yMovement stay within the boundaries(in my example if you press both left arrow and the 'a' key the sprite will move twice as fast :P ), but this is just a rough example to demonstrate how to achieve continuous movement using keyboard events and an update function.
Related
I'm able to rotate the sprite 90-degree angle but unable to change the angle of the physics body. My purpose is to connect multiple sprites with createBlock() to make a breakable tower object. I want to add setRotation() to PhysicsBody::createBox but error showed up and was unable to implement what I want to do. I googled for some solutions but wasn't able to find a helpful solution to my problem. I would love to hear some tips or examples from you!
Here is the function to create a Block sprite. Added 90 so that the sprite will be rotated to 90 degrees.
createBlock(BlockType::Block1, Point(586, 150), 90);
↓
void GameLayer::createBlock(BlockType type, Point position, float angle)
{
std::string fileName;
switch (type)
{
case BlockType::Block1:
fileName = "block1.png";
break;
case BlockType::Block2:
fileName = "block2.png";
break;
case BlockType::Roof:
fileName = "roof.png";
break;
default:
fileName = "stone.png";
break;
}
auto block = Sprite::create(fileName.c_str());
block->setPosition(position);
block->setRotation(angle);
block->setTag(T_Block);
PhysicsBody* body;
switch (type)
{
case BlockType::Block1:
case BlockType::Block2:
{
body = PhysicsBody::createBox(block->getContentSize(), PhysicsMaterial(0.5, 0.5, 0.3));
body->setDynamic(true);
body->setContactTestBitmask(0x01);
break;
}
case BlockType::Roof:
{
Point points[3] = {Point(-50, -25), Point(0, 25), Point(50, -25)};
body = PhysicsBody::createPolygon(points, 3, PhysicsMaterial(0.5, 0.5, 0.3));
body->setDynamic(true);
body->setContactTestBitmask(0x01);
break;
}
default:
{
body = PhysicsBody::createBox(block->getContentSize(), PhysicsMaterial(0.5, 0.5, 0.3));
body->setDynamic(false);
break;
}
}
block->setPhysicsBody(body);
addChild(block, Z_Block);
}
If you want to rotate not only the sprite but also physics, we must call setRotation(angle) after calling block->setPhysicsBody(body).
So, please fix code like below...
1st: block->setPhysicsBody(body);
next: block->setRotation(angle);
last: addChild(block, Z_Block);
I know your code and have experienced same problem before! I have this text book, too.(^ ^)
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
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 );
I have made a flash application in which you need to match an object. So below, I am setting the objects (ship, obj1,obj2,obj3) to targets (t1,t2,t3,t4). I am then setting the score to 0.
private function onAddedToStage(e:Event):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, _check);
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
ship.setTarget(t1,shipObject);
obj1.setTarget(t2,1Object);
obj2.setTarget(t3,2Object);
obj3.setTarget(t4,3Object);
score = 0;
scorecounter.text =" SCORE:" + score.toString();
}
next, I created a function which finds out if the object, in this case the ship, has reached the target.
function _check(event:TimerEvent):void
{
if(ship.done)
{
score += 1;
scorecounter.text =" SCORE:" + score.toString();
}
}
This works correctly, because once the user drags the ship to the target, it adds +1 to the score, but then an issue occurs. I have more than one object/target. This means when the user completes moving the ship to the target, any other objects that become moved instantly give a +1 to the score, because the object ship is still at the target.
Anybody got any ideas how to fix this?
Many Thanks.
Hmmm... a little difficult to capture what is going on without the full code, but here is a shot:
I notice that your "ship" object has a property "done", so I am guessing you created a variable/property on the object called "done", which makes me think that you can add another property to the ship like "scored"
so your code might look something like this:
if ((ship.done) && (ship.scored == false)){
ship.scored = true;
score += 1;
scorecounter.text = "SCORE:" + score.toString();
}
Is that possible for you?
I'm cleaning up some AS3 code today, and want to replace a bunch of messy if/else if/else statements with a switch statement.
private const myConstant:int = 3;
private var someNumber:int = 1000;
for(var i:int=0; i < someNumber; i++){
switch(i, myConstant){
case 0:
function1();
break;
case (i % myConstant == 0):
function2();
break;
default:
function3();
}
}
My program has many more case statements and variables, however, I cut it down for the sake of brevity. In this example, I want to call function2() on every third iteration of the loop. Now, myConstant is an important setting for the class which is used elsewhere, so I can't just put a literal 3 in the expression.
Can I evaluate multiple variables (and constants) in one switch?
Can I evaluate expressions such as the second case statement in my example?
The switch keyword takes only one expression inside the parentheses (your comma separated variables above would not evaluate to one expression).
The case keyword also expects to evaluate one expression.
In your example, it's unnecessary to pass either i or myConstant into the switch statement. These vars are declared immediately above the switch and are accessible to any code inside the switch statement.
Perhaps you want something like this:
for (var i:int = 0, i < someNumber; i++)
{
switch (true)
{
case i == 0:
function1();
break;
case i % myConstant == 0:
function2();
break;
default:
function3();
}
}