In a side-scroller type game, I want the object to move wherever I click the mouse and then stop at that location. What is the best way to accomplish this? The object can only move on the x-axis so I don't have to worry about moving on the y-axis.
What I would do is set a target x coordinate, then move towards it each frame (or timer tick) based on a constant movement speed.
const moveSpeed:Number = 5;
var targetX:Number = 0;
stage.addEventListener(MouseEvent.CLICK, click);
function click(e:MouseEvent):void {
targetX = mouseX;
addEventListener(Event.ENTER_FRAME, update);
}
function update(e:Event):void {
if (Math.abs(targetX - player.x) < moveSpeed) {
// reached target
player.x = targetX;
} else if (targetX > player.x) {
// move right
player.x += moveSpeed;
} else {
// move left
player.x -= moveSpeed;
}
}
Use tweener:
http://hosted.zeh.com.br/tweener/docs/en-us/
And apply a tween like this:
Tweener.addTween(myObject, {_x:myObject.parent.mouseX, time:1, transition:"linear"});
And you can play with the time and the transition type. Good overview of transition types can be found here:
http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html
Related
I'm trying to make an animation using AS3 which consists of dragging and placing movieclips (small images) inside some rectangles (bitmaps) and then presenting the images (big images) corresponding to the movieclips by the same order as they were placed along the rectangles.
However, there is a little trick with which I'm having a few difficulties. When a movieclip is placed inside a rectangle and I try to drag another movieclip to the same rectangle, the one that I'm dragging should return to the initial position. The code I have is working sometimes, but others, it doesn't (still possible to place the movieclip above or under the other).
Another question is: How can I make for the movieclip that I'm dragging to always go above another movieclip and not under? (sometimes they go above, anothers they go under).
Thanks in advance. Best regards.
Here is my code:
function returnToInitial(k:int){
this["foto"+String(k)+"_mc"].x = this["foto"+String(k)+"_mc"].iniX;
this["foto"+String(k)+"_mc"].y = this["foto"+String(k)+"_mc"].iniY;}
for(i=1; i<7; i++){
this["foto"+String(i)+"_mc"].addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
this["foto"+String(i)+"_mc"].num = i;
if(i <= 3){
this["foto"+String(i)+"_mc"].iniX = (160*i)+(i-1)*100;
this["foto"+String(i)+"_mc"].iniY = 100;
}else if(i > 3){
this["foto"+String(i)+"_mc"].iniX = (160*(i-3))+((i-3)-1)*100;;
this["foto"+String(i)+"_mc"].iniY = 260;
}
}
function startDragging(me:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
MovieClip(me.currentTarget).startDrag(true);
currentDragged = MovieClip(me.currentTarget);}
function stopDragging(evt:Event):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
stopDrag();
checkPosition(currentDragged);}
function firstCheck(mcR:MovieClip){
for(q = 0; q < icons.length; q++){
if(this[icons[q]].y == 455){
posX = Math.abs(mcR.x - this[icons[q]].x);
if(posX < 10){
returnToInitial(mcR.num);
}
}
}
}
function checkPosition(mc:MovieClip){
firstCheck(mc);
if(mc.y > 420 && mc.y < 490){
mc.y = 455;
for(k=0; k<6; k++){
if(mc.x > 60+(k*135) && mc.x < 120+(k*135)){
mc.x = 90+(k*135);
array[k] = mc.num;
}
}
}
}
For making the movieclip you are dragging appear on top of everything else, add this to startDragging:
setChildIndex(MovieClip(me.currentTarget), this.numChildren-1);
This is the code i have got so far for a single falling object. The 'DangerIN' is the instance name for the object that is falling down. The class is named 'Danger'. So how can i make it loop so it falls continuously and when it reaches certain y value it will remove it self. Also i want more than one(abount 5) objects falling down at once.
var randomX:Number = Math.random() * 550;
DangerIN.x = randomX;
DangerIN.y = 96;
var speed:Number = Math.random()*10;
DangerIN.addEventListener(Event.ENTER_FRAME, moveDown);
function moveDown(e:Event):void {
e.target.y += speed;
if(e.target.y >= 610) {
DangerIN.removeEventListener(Event.ENTER_FRAME, moveDown);
}
}
It's easy. But to do that, you first need an Array of falling stuff, and then you need to reposition your e.target to the top once it's below your threshold.
function moveDown(e:Event):void {
e.target.y += speed;
if (e.target.y >= 610) {
// reposition
e.target.x=math.random()*550;
e.target.y=96;
}
}
Assign this function to every object you want to fall down, reach bottom and reappear back up.
To remove itself you can add a following line after the removeEventListener():
parent.removeChild(this);
But it's not pretty and you probably should to it the right way:
Store all the Danger objects in the array, in the Danger class create a function like go(), moveDown() or something:
public function go():void
{
y+= speed;
}
and in the class where you create the Danger objects make a loop like this:
private function loop():void
{
for (var i:int = dangerObjArray.lenght - 1; i >= 0; i--)
{
dangerObjArray[i].go();
if (dangerObjArray[i].y >= maxY)
dangerObjArray.splice(i , 1);
}
}
I have a class Catcher which lets you control a movieclip in a game. I'm trying to program the game so it finishes and you can restart. So I need to remove everything and go back to the menu. Should be a simple thing to solve but I can't seem to find out how.
So far I just have ourCatcher.parent.removeChild(ourCatcher); to remove my movieclip from the stage. And an if statement to stop one of the functions which drops things onto the stage. SoundMixer.stopAll(); to stop the music.Then I just have it going to frame 3 which is the gameover screen.
It looks fine but I get constant 1009 errors overflowing in the error console and when I restart the game, it's super slow. It seems the function for movement within Catcher is still running and creating an error because the Catcher was removed from stage and is null now.
I know I need to un-reference everything to do with the Catcher but I can't find out any documentation online to do it in my situation. Everyone seems to have different methods which I've tried and don't work.
The two functions in the Catcher class I'm using to move the character :
public function Catcher(stageRef:Stage)
{
stop();
this.stageRef = stageRef;
key = new KeyObject(stageRef);
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
//movement
public function loop(e:Event):void
{
if (key.isDown(Keyboard.A))
vx -= walkSpeed;
else if (key.isDown(Keyboard.D))
vx += walkSpeed;
else
vx *= friction;
//update position
x += vx;
//speed adjustment
if (vx > maxspeed)
vx = maxspeed;
else if (vx < -maxspeed)
vx = -maxspeed;
//stay inside screen
if (x > stageRef.stageWidth)
{
x = stageRef.stageWidth;
vx = -vx
}
else if (x < 0)
{
x = 0;
vx = -vx;
}
if (key.isDown(Keyboard.A))
{
scaleX = -1;
}
else if (key.isDown(Keyboard.D))
{
scaleX = 1;
}
movement();
// Jumping
jump += gravity;
if (y > stage.stageHeight /1.5)
{
jump = 0;
canJump = true;
}
if (key.isDown(Keyboard.SPACE) && canJump)
{
jump = -10;
canJump = false;
}
y += jump;
}
The other class where I'm removing the things from the stage is called CatchingGame and it has a function which drops objects, I put the game over code there for when playerlives == 0 .
if (playerLives == 0 )
{
stop();
ourCatcher.parent.removeChild(ourCatcher);
SoundMixer.stopAll();
gotoAndStop(3);
}
I've probably made an elementary mistake since this is my first flash game. Any help is greatly appreciated as this is pretty much the last step in finishing my game.
Instead of just removing the child by referencing its parent to remove itself (I had to test to make sure this actually worked). Create a function in same place that you create/instantiate the Catcher that removes first the eventListener ENTER_FRAME, then removes the Catcher.
if (playerLives == 0 ) {
stop();
removeCatcher();
SoundMixer.stopAll();
gotoAndStop(3);
}
// new location in the main code where the catcher is created
function removeCatcher():void {
ourCatcher.cleanUp();
removeChild(ourCatcher);
}
// in the Catcher class
function cleanUp():void {
removeEventListener(Event.ENTER_FRAME, loop);
}
if (ourCatcher.parent) ourCatcher.parent.removeChild(ourCatcher);
else trace("Catcher without a parent still plays! DEBUG THIS!");
Basically, you are most likely losing control flow of your catcher, that is, it seemingly tries to remove itself from the stage twice. After it removes itself first time, its parent becomes null, hence the 1009. And, seeing as you've hit a 2028, the same reason applies, your catcher is no longer a child of anywhere.
I have an issue in simple arkanoid game in updating paddle position, I am using following listener to react:
paddle.addEventListener(Event.ENTER_FRAME, movePaddle)
when movePaddle() function is defined in Main everything is working just fine, but when I refactored code and putted movePaddle() functions into Paddle class and changed listener into:
paddle.addEventListener(Event.ENTER_FRAME, paddle.movePaddle)
The result is paddle changing position every frame between the desired one, and some other value
Below movePaddle() function:
public function movePaddle(event:Event):void
{
// DEBUG
trace(this.x);
trace(this.y);
this.x = mouseX - this.width / 2;
if(mouseX < this.width / 2)
{
this.x = 0;
}
// To much to right
if(this.x >= stage.stageWidth - this.width)
{
// To much to left
this.x = stage.stageWidth - this.width;
}
}
Second question:
Is using ENTER_FRAME event good in terms of optimalisation for games ?
As Cameron mentioned, if you move movePaddle() to the Paddle class itself, mouseX and mouseY will refer to the mouse position within the Paddle MovieClip. That is, if the Paddle is at 50,50 on the stage, and the mouse is at 100, 100 on the stage, the mouse coordinates you will receive will be 50,50.
A safer option is to use the mouseX and mouseY values given by the Stage:
public function movePaddle(event:Event):void
{
if(stage != null)
{
x = stage.mouseX - width / 2;
if(stage.mouseX < width / 2)
{
this.x = 0;
}
// To much to right
if(x >= stage.stageWidth - width)
{
// To much to left
x = stage.stageWidth - width;
}
}
}
Another thing I would do is rather than having this line in the Main class:
paddle.addEventListener(Event.ENTER_FRAME, paddle.movePaddle);
I would instead put that in Paddle's constructor like so:
public function Paddle()
{
addEventListener(Event.ENTER_FRAME, movePaddle);
}
As for performance of ENTER_FRAME, this is fine however in a properly structured game I would have a single ENTER_FRAME handler which loops over an Array of game entities and them updates them all by calling a method on each.
Is there a way to have a MovieClip with startDrag, but to force only horizontal and vertical (i.e. not diagonal) movement?
Here is my solution. It tracks the mouseX and mouseY on click and compares it to last position. Finds out which direction the mouse is mainly moving then moves the object there. You may want to add some extra logic to lock the object to the nearest 10th unit or whatever unit size you want to form a snap grid for use in games or organized placement of the object.
Update: I went ahead and added a snapNearest function to help control the movement.
import flash.events.MouseEvent;
import flash.events.Event;
dragObj.addEventListener(MouseEvent.MOUSE_DOWN, dragIt);
var curX:Number = 0;
var curY:Number = 0;
var oldX:Number = 0;
var oldY:Number = 0;
var gridUnit:Number = 25;
function dragIt(e:MouseEvent):void
{
// set x,y on down
oldX = mouseX;
oldY = mouseY;
// add mouse up listener so you know when it is released
stage.addEventListener(MouseEvent.MOUSE_UP, dropIt);
stage.addEventListener(Event.ENTER_FRAME, moveIt);
trace("Start Drag")
}
function moveIt(e:Event):void
{
// figure out what the main drag direction is and move the object.
curX = mouseX;
curY = mouseY;
// figure out which is the larger number and subtract the smaller to get diff
var xDiff:Number = curX > oldX ? curX - oldX : oldX - curX;
var yDiff:Number = curY > oldY ? curY - oldY : oldY - curY;
if(xDiff > yDiff) {
dragObj.x = snapNearest(mouseX, gridUnit);
}else{
dragObj.y = snapNearest(mouseY, gridUnit);
}
oldX = mouseX;
oldY = mouseY;
}
function dropIt(e:MouseEvent):void
{
//remove mouse up event
stage.removeEventListener(MouseEvent.MOUSE_UP, dropIt);
stage.removeEventListener(Event.ENTER_FRAME, moveIt);
trace("Stop Drag")
}
// snap to grid
function snapNearest(n:Number, units:Number):Number
{
var num:Number = n/units ;
num = Math.round(num);
num *= units;
return num;
}
yes. there are a few options.
A. you can choose to use the startDrag() function and supply it's 2nd parameter with a bounds rectangle for your draggable object. something like;
dragSprite.startDrag(false, new Rectangle(0, 0, 20, stage.stageHeight));
B. you can set your draggable object to listen for mouse events, moving it according to the mouse movements. something like:
dragSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownEventHandler);
function mouseDownEventHandler(evt:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
}
function mouseMoveEventHandler(evt:MouseEvent):void
{
//only move dragSprite horizontally
//dragSprite.y = evt.stageY;
dragSprite.x = evt.stageX;
}
function mouseUpEventHandler(evt:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
}
You could use a modifier key, for instance normal behavior would be horizontal & press down the shift key to move vertically.
function mouseMoveEventHandler(evt:MouseEvent):void
{
if(!evt.shiftKey)
dragSprite.x = evt.stageX;
else
dragSprite.y = evt.stageY;
}
You can only constrain to one axis or the other (using a constraint rectangle) But diagonal movement would be possible in the method you propose, unless you also define some other limits... for example a grid.