Talking about Chronotron game, Actionscript 3, Flash, Game development and Users contributions.
Do you remember Chronotron?
It’s a puzzle platform which wants you to interact with past versions of yourself to solve levels. It’s a very original game, higly recommended
Luis Fernando Silva, author of a Box2D platform engine alternative published on the blog some time ago, created a prototype of this game
« I really liked how this prototype turned out and I hope it might help someone out. Maybe a more creative coder might even improve it!
Anyways, for the past few days, I’ve been wondering how to achieve the cool time-warped NPC that repeats all you did on a past run of a level, like on Chronotron and Mouse 10*, I was shooting some thoughts on FGL chat and the idea of using arrays to store player input in an Array and make the NPC replay it came to mind. I started discussing the idea and it gained shape as I and the chat folks talked about the possibilities.
I decided I already had a good base to start coding on, and I promotly started scripting down in Flash. At the beggining, it was hard to code this, there was some issues with the way the input was ordered on the replay array, so I had to create a function to place it in the correct order, along with an issue the way the NPC interpreted it. But after many issues, troubles, crashes and bug testing, the thing is here! And it works (almost) flawlessly! »
The main file is on the timeline and it’s fully commented:
// Keyboard:
var inp:Input2 = new Input2(stage);
// List of NPCs:
var NPCs:Array = new Array();
// The array we'll store the run, with key presses and their respective intervals:
var Run:Array = new Array();
// The current level run time:
var Interval:int = 0;
// The interval limit (in frames):
var IntervalLimit:int = 300; // 300 frames = 10 seconds on 30fps
// This array stores the start time in which a key was pressed:
var KeyPress:Array = new Array(inp.Keys.length);
// Initialize it:
for(var i:int = 0; i < inp.Keys.length; i++){
KeyPress[i] = 0;
}
// Loop event attachment:
stage.addEventListener('enterFrame', loop);
// The first element of the replay array is always the player
// position at the time the run was started being recording:
Run.push([player.x, player.y]);
// Loop event listener:
function loop(e:Event) : void {
// Span the interval up:
Interval ++;
var ev:Boolean = false;
// You could trim bit this later so it only captures
// used key codes (like arrow keys, WASD, etc)
for(var i:int = 0; i < inp.Keys.length; i++){
// If a key is starting to be pressed now:
if(inp.KeysInterval[i] == 1){
// Change this key statement as being held down:
if(KeyPress[i] == 0){
// Start a 'key down' timer:
KeyPress[i] = Interval;
}
}
// If a key is NOT being pressed now:
else if(inp.KeysInterval[i] == 0){
// Checks if this key was being held down:
if(KeyPress[i] > 0) {
// Pushes the key, start time and interval:
storeKeyEvent(i, KeyPress[i], Interval - KeyPress[i]);
KeyPress[i] = 0;
ev = true;
}
}
}
// Key event, show the array, please (remove the '&& false' to show):
if(ev && false)
trace(Run);
// Interval over? Start over and place an NPC
if(Interval > IntervalLimit){
// Little trick to jump the current key state
// to the next NPC:
for(i = 0; i < inp.Keys.length; i++){
if(KeyPress[i] > 0){
storeKeyEvent(i, KeyPress[i], Interval - KeyPress[i]);
KeyPress[i] -= Interval - 1;
}
}
// Reset the interval:
Interval = 0;
var npc:Mc_NPC = new Mc_NPC();
// Copy the run over to the NPC and init it:
npc.Run = Run.slice();
npc.init();
// Set the label:
npc.label.text = "NPC " + NPCs.length;
// Add the NPC:
addChild(npc);
// pushes the NPC to the NPCs array:
NPCs.push(npc);
// Reset the run over:
Run = new Array();
Run.push([player.x, player.y]);
// Reset the NPCs run, as well:
for(i = 0; i < NPCs.length; i++){
NPCs[i].Runtime = 0;
NPCs[i].init();
}
}
// Run the player:
player.stepPlayer();
// Run the NPCs:
for(i = 0; i < NPCs.length; i++){
NPCs[i].stepNPC();
}
}
// Stores a key event on the 'time-warp' array. I use a function for this
// in order to achieve a correct ordering on the events order:
function storeKeyEvent(key:uint, start:uint, interval:uint) : void {
// If there are no elements on the array, just push it already:
if(Run.length == 1) {
Run.push([key, KeyPress[key], Interval - KeyPress[key]]);
return;
}
// Run the loop backwards, assuming the nearest position is at the end
// of the array:
for(var i:int = Run.length - 1; i >= 0; i--){
// Checks to see the event order:
if(i == 0 || (Run[i][1] < start)){
// If this event happened before the event on [i], insert this event
// after it:
Run.splice(i+1, 0, [key, KeyPress[key], Interval - KeyPress[key]]);
break;
}
}
}
And this is the result:
Move the main rectangle with left and right arrow keys, and when the upper timeline is over, watch for you chrono clone.
Download the source code and let me know if you improve it somehow
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.