Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411275 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 07:51:44 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)AS3 Challenge! - Snake!
Pages: [1] 2
Print
Author Topic: AS3 Challenge! - Snake!  (Read 8003 times)
JMickle
Level 10
*****



View Profile
« on: February 10, 2012, 01:56:33 PM »

ok so I thought it would be great practice and learn-from-each-otherness to have some challenges round here. So, welcome to the first

AS3 Challenge!

This challenge is one you've probably seen before somewhere or other, or even done before! The challenge is to create a working, playable game of snake. It doesn't matter what it looks like or how it exactly plays, but there needs to be 2 key features;
  • Pellets, placed at a new random place every time on is eaten.
  • The snake must grow in length as more pellets are eaten.
You must post the source code and a link to a playable SWF if possible.

This is not a competition!

The idea is to learn from each other! Maybe you find some really clever little hacks to cut back on code, or make it faster. We could have some pretty tine games by the end of this! This is for beginners and experts to learn alike. (if that's possible) Be helpful! Take part!


(I'm working on mine)
Logged

Ashkin
Guest
« Reply #1 on: February 10, 2012, 02:31:08 PM »

Awesome idea. Are libraries (flixel, flashpunk) allowed? Or does that kind of destroy the spirit of the challenge?
Logged
jotapeh
Level 10
*****


View Profile
« Reply #2 on: February 10, 2012, 03:30:22 PM »

Neat, ok. I will probably do this.
Logged
randomshade
Level 1
*

Fastzelda


View Profile
« Reply #3 on: February 10, 2012, 05:26:44 PM »

Tempting...OK, why not.
Logged
cubertron
Level 0
***


View Profile
« Reply #4 on: February 10, 2012, 06:03:00 PM »

Awesome idea. Are libraries (flixel, flashpunk) allowed? Or does that kind of destroy the spirit of the challenge?

same question , libraries allowed or not?
Logged
Moczan
Guest
« Reply #5 on: February 10, 2012, 07:10:39 PM »

I'm 100% for not allowing libraries. I think the whole spirit of a challenge is to create something from scratch, as simple as possible. Using a whole game framework like Flixel of FlashPunk for something that can be done in probably less than 100 lines (a sub-challenge hint!) is a bit overkill  Wink

I will definitely be posting my code this weekend!
Logged
Ashkin
Guest
« Reply #6 on: February 10, 2012, 07:28:59 PM »

I'm 100% for not allowing libraries. I think the whole spirit of a challenge is to create something from scratch, as simple as possible. Using a whole game framework like Flixel of FlashPunk for something that can be done in probably less than 100 lines (a sub-challenge hint!) is a bit overkill  Wink

I will definitely be posting my code this weekend!
Aw. Count me out, then. I have no idea how to use AS3.
Logged
michaelplzno
Level 10
*****



View Profile WWW
« Reply #7 on: February 10, 2012, 09:43:42 PM »

Challenge Accepted.

It took me a little longer to do than I wanted. (Less than 5 hours.)

Also, THE STUPID FLASH IDE TAKES FOCUS FROM THE DEBUGGING PROGRAM SO KEYBOARD INPUT DOESN'T WORK PROPERLY! YOU HAVE TO TURN ON "DISABLE KEYBOARD SHORTCUTS" TO DEBUG YOUR CODE PROPERLY!

That cost me like a half hour. Though I guess there are a lot of things I could rant about for AS3 (I'm a c++ guy, this is really my 3rd AS3 program.)

Source and Playable Version

Hope this helps people. Let me know if you have questions about why I did anything of if something isn't clear. I tried to comment a lot.

Edit: (Oh yeah, I HATE trying to write fewer lines of code for a number of reasons. It is POINTLESS and I could write a long essay about why you shouldn't care about it either and it would be glorious.)
« Last Edit: February 10, 2012, 09:53:33 PM by msilver » Logged

buranmu
Level 0
*


View Profile
« Reply #8 on: February 10, 2012, 11:10:54 PM »

Managed to work it out in a 2 hour sprint -- I expected it to be little faster since I've been working in as3 lately, but good times anyway. So many annoying little quirks. Why doesn't a textfield want to be wide enough to support all characters by default without a custom text format or tweaking the width? Sad

Playable SWF: http://dl.dropbox.com/u/61711542/Snake/Snake.swf
Source: http://dl.dropbox.com/u/61711542/Snake/Snakesrc.zip

Let me know if there are any questions. I didn't really comment my code nicely like msilver did (nice job! -- also, agreed about the lines of code)

-buranmu


EDIT: I forgot to mention, the controls to move are the arrow keys!
Logged
michaelplzno
Level 10
*****



View Profile WWW
« Reply #9 on: February 10, 2012, 11:53:51 PM »

Thanks buranmu but yours is superior. Someone pointed out an issue with mine. I didn't realize you could get more than one key event per frame, and thus it is possible to suicide (go back the way you came) if you jam on the keys.

Does the following line fix it if I add it to my key listener:
Code:
event.stopPropagation();

I also like how yours grows more than just one segment for each pellet. I forgot that was in the original.
Logged

randomshade
Level 1
*

Fastzelda


View Profile
« Reply #10 on: February 11, 2012, 01:55:02 AM »

@msilver,@buranmu: Nice work!

Alright, so I finished mine up. Well, I wanted to add bombs, but am too tried. So yeah, I took a little liberty with classic snake and added a bit of flair (and possibly ruined it.)

Play
Source (includes FlashDevelop project and assets)

I'm aware of a few bugs/ugly glitches but would appreciate hearing about anything you find. Took about 8 hours or so, from start to finish.

Edit: Controls are WASD or Arrow keys  Smiley
« Last Edit: February 11, 2012, 02:10:15 AM by randomshade » Logged
roboprez
Level 1
*


Got to love them pixels


View Profile WWW
« Reply #11 on: February 11, 2012, 03:52:23 AM »

@randomshade Wow, non grid based snake is weird!

Okay I'm starting my entry. Hear back from me soon!
Logged

Moczan
Guest
« Reply #12 on: February 11, 2012, 05:22:06 AM »

Here is my take on non-grid based snake. Managed to do it in less than 2 hours, but it lacks some functionality (won't auto-restart, need to refresh).

Game, control Snake with Left and Right arrows:
http://dl.dropbox.com/u/29254520/Snake/index.html

Source:
http://dl.dropbox.com/u/29254520/Snake/Main.as
Just set the window size to 640 x 480 if you want to compile, FP10 required (change Vector to Array if you want it to run on FP9).
EDIT: fixed collision detection a bit
Also
Edit: (Oh yeah, I HATE trying to write fewer lines of code for a number of reasons. It is POINTLESS and I could write a long essay about why you shouldn't care about it either and it would be glorious.)
I was thinking more in lines of not 'over-engineering' the project, not sacrificing readability for the sake of few lines of code. As you can see, my solution is pretty simple and compact, yet I abstract everything on a function level, especially that it may serve educational purpose for those who don't know AS3 yet.

Quote from: Ashkin
Aw. Count me out, then. I have no idea how to use AS3.
How can you use Flixe/FlashPunk without knowing AS3? The syntax is the same, you just don't have access to additional classes from said frameworks.
« Last Edit: February 11, 2012, 06:00:33 AM by Moczan » Logged
st33d
Guest
« Reply #13 on: February 11, 2012, 05:29:12 AM »

Let's just say that I can't be bothered to port this from Java:

http://robotacid.com/pmobile/snakeAI2/applet/index.html
Logged
JMickle
Level 10
*****



View Profile
« Reply #14 on: February 11, 2012, 05:32:37 AM »

I'm on my iPhone so I can't check anyones out so far, but I'm glad so many people have taken part!

@ashkin: I would suggest having a look at some other peoples code to take a look at how raw as3 would work, but if you want to have a go using flixel or flashpunk, noones stopping you; this is about fun, not beauriocracy Smiley
Logged

Sam
Level 3
***



View Profile WWW
« Reply #15 on: February 11, 2012, 05:42:34 AM »

Does the following line fix it if I add it to my key listener:
Code:
event.stopPropagation();

The .stopPropagation method will prevent that particular event from bubbling any further up the display list.

This reply is about to get off the topic of your particular problem, but hopefully will be useful general Flash event handling stuff.

Say you have a display list like this (anne and conique are direct children of stage, ben is a child of anne)

stage
  |
  |___ anne
  |      |
  |      |___ ben
  |
  |___ conique

 
If a KeyboardEvent is dispatched from Ben it will first trigger any listeners on Ben, then "bubble" up to Anne triggering any listeners on her, then bubble up to stage and trigger any listeners there. Conique will have no idea that the KeyboardEvent ever happened.
This is why if you want to catch all the keyboard events that happen you usually add your listeners to the stage or some other similarly base-level DisplayObject.

If you have a listener on Anne which calls the .stopPropagation method on the event then it will be stopped there, and will not bubble up to the stage.

Some caveats:
Not all events do the bubbling phase; you can see if one does by examining the read-only property bubbles.
Not all events can have their propagation stopped; again you can check by examining the read-only property cancelable.

The Source of KeyboardEvents
Something interesting to know about is which object Flash dispatches KeyboardEvents from. They are dispatched from whatever InteractiveObject is indicated by your stage's focus property, or the stage itself if that property is null. You can detect when focus changes by listening for FocusEvent.FOCUS_IN for an object gaining focus and/or FocusEvent.FOCUS_OUT for an object losing focus. These events bubble, so if you place your listeners on the stage you should hear about all of them (or at least about all the FOCUS_IN events).

You'll find that focus will generally change to whatever InteractiveObject was last clicked on or selected using the tab key (to be more accurate, it changes when you click on a TextField, but will revert to null on clicking a Sprite - I'm unsure how exactly it is determined what objects will automatically shift focus). The reason for this is so that textfields and textfield-like things can be sure that they receive keyboard input after they've been selected by the user.

Most of the time this doesn't matter in games, as we're just listening on the stage, and bubbling events always eventually reach the stage, right? Almost always. Say Ben is the focus, and he is removed from the display list. KeyboardEvents are still going to be dispatched from him, but as he's now disconnected from the display list they will have nowhere to bubble to and will go unheard by our listeners on the stage. Until the user next clicks somewhere, you'll miss out on all their keyboard input.

There's two ways to prevent this:
Make a little utility class that listens on the stage for FOCUS_IN events and keeps track of that object, manually resetting focus to null if that object dispatches an Event.REMOVED_FROM_STAGE. Just make sure you don't end up leaving event listeners attached to objects all over the place.
Simply set stage.focus = null every frame. Simple, but that will totally break any input TextFields you might be using.

And now back to the actual question at hand.

Basically, no.

Simplest solution would be to have a Boolean variable hasTurnedThisUpdate, set to false at the start of each frame and have your event listeners check that it's false before turning the snake (and set it to true once they do). Problem is if a user presses two different directions in quick succession this will effectively ignore the second input, which leads to controls feeling buggy and unresponsive.

So instead! You'd want to create a queue structure to hold incoming input commands. Using an Array or Vector should be fine (if you're chasing performance queues are best implemented as linked lists but it'll really not matter in this case), and have your KeyboardEvent listener just .push the event it receives on to the back of the queue. Your main game loop will then .shift the front event from the queue and deal with it. This way there's no lost keyboard events, and you always have a maximum of one event to deal with each update.

Problems with this solution that are "left as an exercise for the reader":
If the user mashes a load of unrelated buttons and then presses the key to turn Left, your game loop will take several frames before it gets around to dealing with the Left command. Laggy controls!
Similarly (and slightly less contrived) if the player is holding down the Down key and then presses Left then depending on their system your queue may be filled with dozens of Down key events which will delay reception of the Left key event. Again leading to the feeling of laggy controls.

(You'll want to either filter what key events are added to the queue by your listener, or allow the game loop to intelligently deal with multiple key events each loop - but without reintroducing the original instant U-turn problem.)
Logged
Moczan
Guest
« Reply #16 on: February 11, 2012, 06:30:18 AM »

You really should just have bools for every direction (like LEFT and RIGHT in my code), check them in your main loop in if...else conditional tree, excluding direction opposite to the current one. It won't accidentally kill you and will let you do fast u-turns.
Queues are useful if you need to store input chains for combos in fighter games etc. no need to implement such complex thing in such simple game as Snake.
Logged
Sam
Level 3
***



View Profile WWW
« Reply #17 on: February 11, 2012, 06:55:02 AM »

This issue is if in a traditional grid-based Snake the player's currently moving up, then presses Right then Down so fast that they occur within a single frame length.

For instance if you're doing something like this for handling input:
Code:
static const DIR_RIGHT:int = 0;
static const DIR_UP:int =    1;
static const DIR_LEFT:int =  2;
static const DIR_DOWN:int =  3;

var keyCodeForRight:int = 39;
var keyCodeForUp:int    = 38;
var keyCodeForLeft:int  = 37;
var keyCodeForDown:int  = 40;

var currentDirection:int = DIR_UP;

function keyEventListener(e:KeyboardEvent):void
{
   switch (e.keyCode)
   {
      case keyCodeForRight:
         if (currentDirection != DIR_LEFT)  currentDirection = DIR_RIGHT;
         break;
      case keyCodeForUp:
         if (currentDirection != DIR_DOWN)  currentDirection = DIR_UP;
         break;
      case keyCodeForLeft:
         if (currentDirection != DIR_RIGHT) currentDirection = DIR_LEFT;
         break;
      case keyCodeForDown:
         if (currentDirection != DIR_UP)    currentDirection = DIR_DOWN;
   }
}

function perFrameUpdate():void
{
   switch (currentDirection)
   {
      case DIR_RIGHT:
         playerX++;
         break;
      case DIR_UP:
         playerY--;
         break;
      case DIR_LEFT:
         playerX--;
         break;
      case DIR_DOWN:
         playerY++;
   }
}

Then it is possible with sufficiently fast key presses and sufficiently low framerate for keyEventListener to receive an event for the right key and an event for the down key before perFrameUpdate is called. As the events are received by keyEventListener in the correct order its "no doubling back" logic will still allow it.

However as the player hasn't actually moved between those two inputs the result is that the player performs a suicidal instant U-turn.

You can avoid it by adding a limit of one turn per frame.  But as I've said above that leads to the game ignoring user input, hence the need for a queue of some description.

Edit:
I should add that if you shift the "no doubling back" logic to the perFrameUpdate function, like so:

Code:
static const DIR_RIGHT:int = 0;
static const DIR_UP:int =    1;
static const DIR_LEFT:int =  2;
static const DIR_DOWN:int =  3;

var keyCodeForRight:int = 39;
var keyCodeForUp:int    = 38;
var keyCodeForLeft:int  = 37;
var keyCodeForDown:int  = 40;

var directionFromInput:int = DIR_UP;
var currentDirection:int = DIR_UP;

function keyEventListener(e:KeyboardEvent):void
{
   switch (e.keyCode)
   {
      case keyCodeForRight:
         directionFromInput = DIR_RIGHT;
         break;
      case keyCodeForUp:
         directionFromInput = DIR_UP;
         break;
      case keyCodeForLeft:
         directionFromInput = DIR_LEFT;
         break;
      case keyCodeForDown:
         directionFromInput = DIR_DOWN;
   }
}

function perFrameUpdate():void
{
   // handle input
   switch (directionFromInput)
   {
      case DIR_RIGHT:
         if (currentDirection != DIR_LEFT)  currentDirection = DIR_RIGHT;
         break;
      case DIR_UP:
         if (currentDirection != DIR_DOWN)  currentDirection = DIR_UP;
         break;
      case DIR_LEFT:
         if (currentDirection != DIR_RIGHT) currentDirection = DIR_LEFT;
         break;
      case DIR_DOWN:
         if (currentDirection != DIR_UP)    currentDirection = DIR_DOWN;
   }

   // actually move the player
   switch (currentDirection)
   {
      case DIR_RIGHT:
         playerX++;
         break;
      case DIR_UP:
         playerY--;
         break;
      case DIR_LEFT:
         playerX--;
         break;
      case DIR_DOWN:
         playerY++;
   }
}

Then you avoid the possibility of suicidal instant U-turns, but the game will again ignore some input. In the danger case of the player travelling up and rapidly pressing Right then Down, when perFrameUpdate is called directionFromInput will be DIR_DOWN as that was the last keyboard input received. The result will be that the "no doubling back" logic detects a deadly U-turn, and will refuse to make the turn.

However that means that the game has now ignored two valid user key-presses, with the snake continuing on up after the user has instructed it to turn right and down. Even more infuriating for our user, if she'd made the exact same input but a few milliseconds earlier or later then the two inputs would have fallen in different update cycles and the game would have responded correctly. A classic "sometimes the controls just don't work at all" bug.
« Last Edit: February 11, 2012, 07:19:12 AM by Sam » Logged
Moczan
Guest
« Reply #18 on: February 11, 2012, 09:11:44 AM »

Sam you still ignored my solution using Boolean variables. If you check your logic against input directly in KeyboardEvent handler, you will always approach problems as it's not the way you should do it.

Implementing a simple getClick (or rather queueClick would be better name) method solves this problem.

Pseudo code of it would be something like
Code:
function keyDown(e.KeyboardEvent):void{
  keyClick[e.keyCode] = true;
}
function getClick(arg:uint):Boolean{
  if(keyClick[arg]){
    keyClick[arg] = false;
    return true;
  }
  return false;
}
Now if you check the movement in your game loop it will handle the fast turns situations without any problems and suicide moves.

Now if you check the movement in your game loop it will handle the fast turns situations without any problems and suicide moves.

If there is anything wrong, it's the input check in gameloop, not the KeyboardEvent handler. You are solving problems in a wrong place Wink
« Last Edit: February 11, 2012, 09:29:45 AM by Moczan » Logged
randomshade
Level 1
*

Fastzelda


View Profile
« Reply #19 on: February 11, 2012, 09:57:36 AM »


This is probably my favorite one so far. Feels traditional in terms of control/movement while being off the grid.

@ashkin: I would suggest having a look at some other peoples code to take a look at how raw as3 would work, but if you want to have a go using flixel or flashpunk, noones stopping you; this is about fun, not beauriocracy Smiley

Yeah, I'm more interested in seeing interesting ideas than anything else here. If you take a look at my code (which isn't amazing, but works) I set up everything as a sort of ultra-lite game engine. So a lot less functionality than Flixel or whatever, but takes care of some of the menial stuff.

Edit: I should add that I'm not an expert in AS3 and it's not my primary dev language. I've just been tinkering with it for a while now.
« Last Edit: February 11, 2012, 10:03:51 AM by randomshade » Logged
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic