as3 slope detection for non uniform terrains - actionscript-3

I want to create a 2d game in which a car moves along the edge of an irregular shaped hill. I want to use basic as3 (no physics engine like Box2d or Nape). With a lot of research I found this thing which is exactly what i want but there is only the logic and no source code. Can someone help me with a piece of code that can do this in as3? Also suggest if there is a better alternative to get the desired output.

I myself don't have much knowledge into code like this, and I can't make a comment because I'm short 7 reputation :'( but the article you linked does have another link to a Worms-esque program with code that might be of help to you. Here's a link to that site in question and I'll also provide some appropriate code that you can see if it's of use to you.
So this program has a listener for if any key is down and sets a boolean as true if it's a key your program is using (movement, space for jumping or whatever), that listener looks like this
public function key_down(e:KeyboardEvent) {
if (e.keyCode==37) {
left_key=true;
}
if (e.keyCode==39) {
right_key=true;
}
if (e.keyCode==32) {
space_key=true;
}
}
And then it has a subsequent listener for when a key is released
public function key_up(e:KeyboardEvent) {
if (e.keyCode==37) {
left_key=false;
}
if (e.keyCode==39) {
right_key=false;
}
if (e.keyCode==32) {
space_key=false;
}
}
And finally the stage has an ENTER_FRAME listener for moving the character that runs this function
public function move_character(e:Event) {
//If left key is pressed, we'll move the character to the left
if (left_key) {
for (i=0; i<3; i++) {//Do you remember when we made the character fall? We had to move the character pixel by pixel
if (! terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x-6,character.y-10,1,17))) {
character.x--; /*If the character doesn't hit the ground, we can move left. However,
the character may be sunk under the ground. We have to lift it*/
while (terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x-5,character.y+9,10,1))) {
character.y--;
}
}
}
}
if (right_key) {//Well, that's the same for the right key
for (i=0; i<3; i++) {
if (! terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x+5,character.y-10,1,17))) {
character.x++;
while (terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x-5,character.y+9,10,1))) {
character.y--;
}
}
}
}
if (space_key&&! jumping) {//That's easy: if he isn't jumping and you press space, his speed will be negative and he'll jump
character_speed=-10;
jumping=true;//Now the character can't jump again
}
character_speed++;//Every frame we will increase character's speed
if (character_speed>0) {
//If the speed is positive, we will check a collision between the terrain and the rectangle below the character
for (i=0; i<character_speed; i++) {//We check the collision pixel by pixel...
if (! terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x-5,character.y+9,10,1))) {
character.y++;//If there isn't a collision, the character will fall
} else {
jumping=false;//If there's a collision with the ground, the character isn't jumping
character_speed=0;//The speed is 0, because the character hit the ground
}
}
} else {
for (i=0; i<Math.abs(character_speed); i++) {//If the speed is negative, the for loop won't work. We have to use Math.abs().
//Now we will check the collision between the terrain and the rectangle above the character
if (! terrain_bmpd.hitTest(new Point(terrain_bmp.x,terrain_bmp.y),0x01,new Rectangle(character.x-5,character.y-10,10,1))) {
character.y--;
} else {
character_speed=0;//Well, that's the same: the character hit the ground
}
}
}
}
Sorry I couldn't provide any personal experience, if you want more then the article I linked above will provide more information.

Related

AS3 How to reload a game?

This code is an attempt to reset the stage to the original setup and start the game over, however it would only delete everything of the stage and leave it blank, it wont load the items on the stage. what am I missing here?
playLvlAgn.addEventListener(MouseEvent.CLICK, doLevelAgain);
function doLevelAgain(event: MouseEvent): void {
stage.removeChildren();
gotoAndPlay(1);
}
Just because things visually go away, it doesn't mean the objects in memory are magically purged.
You would need to handle this resetting yourself. As others have pointed out, to do this, it highly depends on your game's code and how it is organised. We simply can't provide any meaningful help as the possibilities for a game can be near infinite in the ways it is written.
As a somewhat related side thing:
If you are going to stick with frames (which others may be against, but I personally see them as useful in a variety of situations), I'd advise you to not start everything on frame 1. Again, I don't know how you've written your game, but it might be helpful to separate out certain functionality on certain frames.
Just an example:
Maybe asset loading can be on one frame (let's say frame 1)
Then on the next frame perhaps global persistent ui is created, such as help/settings menus. (frame 2)
Then the game level itself such as maps and layout + stuff that survives a reset can be built on the next (frame 3)
Then perhaps game play specific data + stuff that does not survive a reset, say player score and game time (frame 4)
Then the game starts (frame 5)
Then for a reset, if you want to just reset that level, you can do all your resetting/clean up logic, then jump to frame 4 and avoid reloading assets, recreating persistent ui + the physical game level.
It can get quite hairy if you weren't planning for this and in some cases, you may need to rethink/reorganise/rewrite some of your code. This is not a bad thing.
OK I found the solution. all removeChilds had to be inside the button function. I also had to sent it to frame2 and stop just in case someone attempted to reload the game before it was even started to play.
playLvlAgn.addEventListener(MouseEvent.CLICK, doLevelAgain);
function doLevelAgain(event: MouseEvent): void {
gotoAndStop(2);
for (var i: int = 0; i < 3; i++) {
if (balls.length > 0) {
ball = balls.pop();
ball.parent.removeChild(ball);
trace("if--0c")
}
}
if (stage.contains(playLvlAgn)) {
removeChild(playLvlAgn);
trace("if--1")
}
if (stage.contains(yesh8DoorA)) {
removeChild(yesh8DoorA);
trace("if--2")
}
if (stage.contains(yesh8DoorB)) {
removeChild(yesh8DoorB);
trace("if--3")
}
if (stage.contains(yesh8DoorC)) {
removeChild(yesh8DoorC);
trace("if--4")
}
if (stage.contains(yesh8allBuckets1)) {
removeChild(yesh8allBuckets1);
trace("if--5")
}
if (stage.contains(yesh8allBuckets2)) {
removeChild(yesh8allBuckets2);
trace("if--6")
}
if (stage.contains(add8bigGrayBall6)) {
removeChild(add8bigGrayBall6);
trace("if--7")
}
if (stage.contains(yesh8wall5)) {
removeChild(yesh8wall5);
trace("if--8")
}
if (stage.contains(yesh8step)) {
removeChild(yesh8step);
trace("if--9")
}
if (stage.contains(yesh8bomb26)) {
removeChild(yesh8bomb26);
trace("if--10")
}
if (stage.contains(yesh8bomb27)) {
removeChild(yesh8bomb27);
trace("if--10")
}
if (stage.contains(yesh8bomb28)) {
removeChild(yesh8bomb28);
trace("if--10")
}
if (stage.contains(yesh8bomb2)) {
removeChild(yesh8bomb2);
trace("if--11")
}
if (stage.contains(yesh8bomb3)) {
removeChild(yesh8bomb3);
trace("if--12")
}
if (stage.contains(yesh8gBall21)) {
removeChild(yesh8gBall21);
trace("if--13")
}
if (stage.contains(yesh8gBall24)) {
removeChild(yesh8gBall24);
trace("if--14")
}
if (stage.contains(yesh8redHalfSwing)) {
removeChild(yesh8redHalfSwing);
trace("if--15")
}
gotoAndPlay(1);
}

AS3: Fast hovering doesn't execute rollOut

I'm having a serious problem that is getting me nervous:
I've made a button _btn that includes ROLLOVER and ROLLOUT animations with coding (an nested movieclip instance called barra that increases to half alpha when you hover over and decreases when you hover out).
[Here it should go a descriptive image but I'm new and I need 10 reputation. I'll appreciate your help]
This works perfectly but the problem occurs when I move my cursor very quickly from one point to another, with the button in between. It seems that the ROLLOUT function is not detected, so the ROLLOVER animation keeps working (and if you look carefully, the animation stops for a few seconds and then continues).
[Here it should go another descriptive image too]
This is the code in the Actions layer:
//Funciones ROLL OVER
function _btnOver(event:MouseEvent):void {
_btn.buttonMode = true;
_btn.addEventListener(Event.ENTER_FRAME,_btnFadeIn);
}
function _btnFadeIn(event:Event):void {
_btn.barra.alpha += 0.1;
if (_btn.barra.alpha >= 0.5)
{
_btn.removeEventListener(Event.ENTER_FRAME,_btnFadeIn);
}
}
_btn.addEventListener(MouseEvent.ROLL_OVER,_btnOver);
//Funciones ROLL OUT
function _btnOut(event:MouseEvent):void {
_btn.addEventListener(Event.ENTER_FRAME,_btnFadeOut);
}
function _btnFadeOut(event:Event):void {
_btn.barra.alpha -= 0.1;
if (_btn.barra.alpha <= 0.2)
{
_btn.removeEventListener(Event.ENTER_FRAME,_btnFadeOut);
}
}
_btn.addEventListener(MouseEvent.ROLL_OUT,_btnOut);
Click here if you want to download the FLA and SWF files, so you can see the problem clearly.
I barely know how to use ActionScript 3 (my only programming knowledge is Processing) and I don't have time now to learn it from head to toe, but I've researched about the problem and it's still not clear.
With tutorials and guides, I managed to learn how to create and understand this code, and I think the problem might be in the functions of the events ROLL_OVER and ROLL_OUT, which contain the addEventListener of the ENTER_FRAME events (where the animations actually are), respectively. But I don't know exactly what I have to do to fix it, what should I add or change.
I would be really glad if someone could help with this, I'm frustrated! What do you recommend me to do?
Thanks in advance
(PD: I don't understand most of the programming language. If you can be as clear and direct as possible, I'll really appreciate it)
Apparently your troubles lay in incoherent animation sequence by using enter frame listeners. You are running two independent listeners, both altering alpha of a single object, this creates a conflict, only one will work (you can determine which if you add both at once and trigger events, the resultant alpha value will indicate which listener changes it last) and you apparently expect one to do a fade in while the other to do a fade out. Instead, you should use one listener (probably even persistent) and give your object "target alpha" property as well as delta to change alpha per frame. An example:
var bbta:Number=0.2; // btn.barra's target alpha
_btn.addEventListener(Event.ENTER_FRAME,_btnFade);
function _btnFade(e:Event):void {
var a:Number=_btn.barra.alpha;
if (Math.abs(a-bbta)<1e-8) return;
// no sense of setting alpha with minuscule difference
const delta:Number=0.1; // how fast to change per frame
if (a>bbta) {
a-=delta;
if (a<=bbta) a=bbta;
} else {
a+=delta;
if (a>=bbta) a=bbta;
}
_btn.barra.alpha=a;
}
function _btnOver(event:MouseEvent):void {
_btn.buttonMode = true; // move this elsewhere, if you don't cancel buttonMode
bbta=0.5; // set target alpha, the listener will do a fade-in
}
function _btnOut(event:MouseEvent):void {
bbta=0.2; // set target alpha, the listener will do a fade-out
}
I edited some code in here, basically i am checking hover state onLoop function, so you can change your settings on here
import flash.events.Event;
var isRolledOver:Boolean = false;
//Funciones ROLL OVER
function _btnOver(event:MouseEvent):void {
isRolledOver = true;
}
function _btnOut(event:MouseEvent):void {
isRolledOver = false;
}
_btn.addEventListener(MouseEvent.ROLL_OVER,_btnOver);
_btn.addEventListener(MouseEvent.ROLL_OUT,_btnOut);
this.addEventListener(Event.ENTER_FRAME,onLoop);
function onLoop(e){
if(this.isRolledOver){
if(_btn.barra.alpha < 0.5) _btn.barra.alpha += 0.1;
}
else{
if(_btn.barra.alpha > 0.5 || _btn.barra.alpha > 0) _btn.barra.alpha -= 0.1;
}
}
I added the sample fla in case

AS3: repeating function in sigle frame timeline

I'm very new in action script. I have single frame timeline and there is function that moves movie clip verticaly. i want to repeat this only three times.
The code works, I'm just not sure if this is the correct way or if it's too complicated.
var pocet:Number = 0;
pruh.addEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
function fl_AnimateVertically(event:Event)
{
if (pruh.y >= stage.stageHeight) {
pocet++;
}
if (pruh.y < stage.stageHeight) {
pruh.y += 3;
}
else {
pruh.y = 0 - pruh.y;
}
if (pocet == 3) {
pruh.removeEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
}
}
thanx
Congratulations on achieving your goal.
Your code could be improved in terms of readability. You have fl_AnimateVertically as a descriptive name, but other than that it's kind of hard to figure out what's going on exactly. I mean sure it adds 3 to y which probably results in movement, but it's not trivial to understand the exact behaviour.
That's why you want to use abstraction or more of a top down approach as it often called..
What you are doing at the moment is adding a value to the coordinate, which as a result creates an animation. What you actually want is to create an animation, without going into details what that actually means.
And sure enough, people created animations with code before. That's why you can create an animation in the abstract sense: An animation is the change of a property of an object over time. In the realm of flash an animation is called a tween and there's a class doing exactly that..
Let's take the example code there:
var myTween:Tween = new Tween(myObject, "x", Elastic.easeOut, 0, 300, 3, true);
And apply it to your situation.
var verticalAnimation:Tween = new Tween(pruh, "y", Elastic.easeOut, pruh.y, stage.stageHeight, 3, true);
You have to adjust the duration to your liking. I hope you see how this is easier to read and maintain, because you specify properties of the animation like duration. You can also specify easing, which makes the motion more interesting.
Ok, this is only one animation, but you want 3, right?
More precisely, you want to do that same animation again, when it finished.
And you can do exactly that:
var animationCount:uint = 0;
var verticalAnimation:Tween = new Tween(pruh, "y", Elastic.easeOut, pruh.y, stage.stageHeight, 3, true);
verticalAnimation.addEventListener(TweenEvent.MOTION_FINISH, onMotionFinish); // wait for the animation to be finished
function onMotionFinish(e:TweenEvent):void
{
animationCount++; // add 1 to the counter
if(animationCount >= 3) // check how many times the animation finished so far
{
// if it was the last one, remove the listener
verticalAnimation.removeEventListener(TweenEvent.MOTION_FINISH, onMotionFinish);
}
else
{
// otherwise rewind and start again
verticalAnimation.rewind();
verticalAnimation.start();
}
}
There are other libraries than this built in Tween class that are far more powerful.
The one from greensock is very popular and easy to use you can find the documentation for the flash version here
Try this
var pocet:Number = 0;
pruh.addEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
var startY:int=pruh.y;
function fl_AnimateVertically(event:Event)
{
if (pruh.y >= stage.stageHeight) {
pocet++;
pruh.y=startY;
}
if (pruh.y < stage.stageHeight) {
pruh.y += 3;
}
else {
pruh.y = 0 - pruh.y;
}
if (pocet ==3) {
pruh.removeEventListener(Event.ENTER_FRAME, fl_AnimateVertically);
trace("done");
}
}

How to make a stick man running when pressing a key?

I created a movieclip named stickman. In that, I created an animation by drawing a sequence of move in everyframe, so that stickman can run. Now what I want is that when I press a key, the stick man will run from left to right and when I release the key, it will stop. This is my code:
RunningMan.stop();
stage.addEventListener(KeyboardEvent.KEY_DOWN,keypresseddown);
function keypresseddown(event:KeyboardEvent):void
{
var key:uint = event.keyCode;
switch (key) {
case Keyboard.LEFT :
{
RunningMan.play();
RunningMan.x-=10;
RunningMan.scaleX=-1;
if(RunningMan.x<=0)
{
RunningMan.x=0;
}
};
case Keyboard.RIGHT :
{
RunningMan.play(); //play animated run
RunningMan.x+=10;
RunningMan.scaleX=1;
if(RunningMan.x>=stage.width)
{
RunningMan.x=stage.width;
}
};
default: RunningMan.stop();
}
}
However, when I pressed and held a key, it moved from left to right without animated run.
How can I fix it?
Thanks in advance.
EDIT:
I have a movieclip called character containing 3 movieclip named: standing, running and jumping, respectively. When I pressed up arrow key, it would jump, but if I released the key right away, it did not jump high as the jump movieclip could not finish its frames. This is the code:
if (key.isDown(Keyboard.LEFT))
{
gotoAndStop("running");
BGround.x+=speed;
scaleX=-1;
if(BGround.x>=stage.stageWidth)
BGround.x=stage.stageWidth;
}
else if (key.isDown(Keyboard.RIGHT))
{
gotoAndStop("running");
BGround.x -= speed;
scaleX=1;
}
else
if (key.isDown(Keyboard.UP))
{
gotoAndStop("jumping");
}
else
gotoAndStop("standing");
How can I fix that?
First of all, I hope RunningMan is an instance of the class, not the class itself. And if it is an instance, you should really follow common naming conventions for when you share your code with others (like you are doing now) so it would be runningMan.
So 1st, make the 1st frame of the runnigMan's timeline a picture of the man standing still and name it "still" or something. then name the second "running" and extend that like 20 frames or however long your animation is. at the last frame you will have to use timeline code. just one line of gotoAndPlay("running") will cause those frames of 2 to 20 (or whatever) to loop. When you tell the timeline to go to frame 1 from outside the timeline code, it wont loop anymore and will stay on the frame of the man standing still. So from outside when you want the loop to start:
runningMan.gotoAndPlay("running"); // frame 2
To stop:
runningMan.gotoAndStop("still"); // frame 1
Or you could do it from inside the RunningMan class
public function startRunAnimation():void{
this.gotoAndPlay("running");
}
public function stopRunAnimation():void{
this.gotoAndStop("still");
}
And you could use them just by replacing these function names with the ones you have if your code ex( instead of play() it would be startRunAnimation() )
EDIT
What you could do for this problem is to have a boolean variable for when your character is in the air (somewhere in your code where you do collision detection with the ground or where you handle gravity - however it is set up) so that this part of your code know when your character is in the air. And then you could simple test for this however way you need it.
...
if (key.isDown(Keyboard.UP) || this.inAir==true)
{
gotoAndStop("jumping");
}
else
gotoAndStop("standing");
Although if your character does not inheirt from a collidable object that has gravity, friction etc... then you would have to make the inAir property of whatever other class, public or make getter function for it - so that you can access it here. I hope this helps.

ActionScript 3: Bullet Ricocheting

I've been having a problem with my Actionscript code. I am fairly new to Flash and AS3, so I apologize if my code seems crude or rudimentary, but I'm doing this as best as I can.
Well, in this project I'm trying to get a bullet to ricochet off a wall once. If it hits a wall again after ricocheting, the bullet will disappear.
I've created a for loop which moves the bullets, in an array. At the same time, I try to keep track of each bullet's individual number of ricochets. This works fine when I shoot a first bullet - it will ricochet and then disappear after hitting another wall. However, every bullet I fire after that disappears on the first wall it hits, before it has ricocheted. I've tried to get this to work but I just can't seem to do it.
I would be grateful if somebody could show me the problem, or suggest a change to my code.
Here is a link to my code as it is now.
Thanks, to anybody who helps.
Here are some suggestions I have:
1: Create a Bullet class that tracks its own collisions against walls. I'd also move the clearBullet() method into the bullet class itself.
public class Bullet extends Sprite
{
public var collisions:int = 0;
public var xv:Number = 0;
public var yv:Number = 0;
public function clear():void
{
if(parent)
parent.removeChild(this);
}
}
2: Update your loop to deal with this new info.
for each(var i:Bullet in bulletholder)
{
// Move bullet.
// Check for collision.
// When there is a collision, do this:
i.collisions ++;
if(i.collisions >= 2)
{
var n:int = bulletholder.indexOf(i);
bulletholder.splice(n, 1);
i.clear();
}
else
{
// Deal with changing bullet position.
}
}
I see at least a couple of problems with your code:
Your ricochetcount is clearly out of sync. i.e. you need to delete an element from that array as well.
When you delete an element from the bulletholder array (via clearBullet), you're still incrementing i, which means you end up inadvertently skipping an element.
Also I'm not sure why you need clearBullet(). You already have the index i as well as a reference to the bullet object right there in the main loop.