I have a 20 frame bouncing ball movieclip “ballAnim” on frame 1 of the stage, and I simply want to count each time “ballAnim” loops playback.
I am inexperienced with actionscript, and I’ve searched and tried a few things to no avail.
Here’s the code from my latest attempt:
import flash.events.Event;
var loopCounter:int;
ballAnim.addEventListener(Event.ENTER_FRAME, addOneLoop);
function addOneLoop(e:Event){
if(ballAnim.currentFrame == 20)
{loopCounter+1}
}
trace(loopCounter);\`
All I get is a single instance of 0. I’ve searched around and I think the problem is that loopCounter is resetting to 0 on every frame because of ENTER_FRAME? I tried addressing this by adding a 2nd keyframe on my actions timeline layer with stop(); (with the movieclip layer spanning both frames underneath) but it doesn’t help.
I’ve read that I might need to use a class, but I’m not sure what that is, and I thought this would be a fairly straightforward thing.
Ok, let's have a class.
Your problem is not understanding the flow of things. If we put it simply, Flash Player executes the movie/application in the infinite loop of recurring phases:
Playheads of playing MovieClips (also, the main timeline) move to the next frame.
Frame scripts are executed.
The whole movie is rendered and the picture is updated on the screen.
Pause till the next frame.
Events are handled.
Ok, the exact order just MIGHT be different (it is possible to figure it out but not important now). The important part is to understand that:
Flash Player is (normally) not a multi-thread environment, the phases follow each other, they never overlap, only one thing at a time happens ever and we are pretty much able to follow, which one.
The script you provided is executed at the "frame scripts" phase and that the ENTER_FRAME event handler doesn't execute until the "event handling" phase kicks in.
So, let's check it:
import flash.events.Event;
// This one returns time (in milliseconds) passed from the start.
import flash.utils.getTimer;
trace("A", getTimer());
ballAnim.addEventListener(Event.ENTER_FRAME, addOneLoop);
// Let's not be lazy and initialize our variables.
var loopCounter:int = 0;
function addOneLoop(e:Event):void
{
trace("BB", getTimer());
// Check the last frame like that because you can
// change the animation and forget to fix the numbers.
if (ballAnim.currentFrame >= ballAnim.totalFrames)
{
trace("CCC", getTimer());
// Increment operation is +=, not just +.
loopCounter += 1;
}
}
trace("DDDD", getTimer());
trace(loopCounter);
Now once you run it, you will get something like this:
A (small number)
DDDD (small number)
0
BB ...
BB ...
(20 times total of BB)
BB ...
CCC ...
BB ...
BB ...
Thus, in order to trace the number of loops happened, you need to output it inside the handler rather than in the frame script:
import flash.events.Event;
import flash.utils.getTimer;
ballAnim.addEventListener(Event.ENTER_FRAME, addOneLoop);
var loopCounter:int = 0;
function addOneLoop(e:Event):void
{
if (ballAnim.currentFrame >= ballAnim.totalFrames)
{
loopCounter += 1;
// This is the very place to track this counter.
trace("The ball did", loopCounter, "loops in", getTimer(), "milliseconds!");
}
}
Related
I am creating a very naive AI (it maybe shouldn't even be called an AI, as it just tests out a lot of possibilites and picks the best one for him), for a board game I am making. This is to simplify the amount of manual tests I will need to do to balance the game.
The AI is playing alone, doing the following things: in each turn, the AI, playing with one of the heroes, attacks one of the max 9 monsters on the battlefield. His goal is to finish the battle as fast as possible (in the least amount of turns) and with the fewest amount of monster activations.
To achieve this, I've implemented a think ahead algorithm for the AI, where instead of performing the best possible move at the moment, he selects a move, based on the possible outcome of future moves of other heroes. This is the code snippet where he does this, it is written in PHP:
/** Perform think ahead moves
*
* #params int $thinkAheadLeft (the number of think ahead moves left)
* #params int $innerIterator (the iterator for the move)
* #params array $performedMoves (the moves performed so far)
* #param Battlefield $originalBattlefield (the previous state of the Battlefield)
*/
public function performThinkAheadMoves($thinkAheadLeft, $innerIterator, $performedMoves, $originalBattlefield, $tabs) {
if ($thinkAheadLeft == 0) return $this->quantify($originalBattlefield);
$nextThinkAhead = $thinkAheadLeft-1;
$moves = $this->getPossibleHeroMoves($innerIterator, $performedMoves);
$Hero = $this->getHero($innerIterator);
$innerIterator++;
$nextInnerIterator = $innerIterator;
foreach ($moves as $moveid => $move) {
$performedUpFar = $performedMoves;
$performedUpFar[] = $move;
$attack = $Hero->getAttack($move['attackid']);
$monsters = array();
foreach ($move['targets'] as $monsterid) $monsters[] = $originalBattlefield->getMonster($monsterid)->getName();
if (self::$debug) echo $tabs . "Testing sub move of " . $Hero->Name. ": $moveid of " . count($moves) . " (Think Ahead: $thinkAheadLeft | InnerIterator: $innerIterator)\n";
$moves[$moveid]['battlefield']['after']->performMove($move);
if (!$moves[$moveid]['battlefield']['after']->isBattleFinished()) {
if ($innerIterator == count($this->Heroes)) {
$moves[$moveid]['battlefield']['after']->performCleanup();
$nextInnerIterator = 0;
}
$moves[$moveid]['quantify'] = $moves[$moveid]['battlefield']['after']->performThinkAheadMoves($nextThinkAhead, $nextInnerIterator, $performedUpFar, $originalBattlefield, $tabs."\t", $numberOfCombinations);
} else $moves[$moveid]['quantify'] = $moves[$moveid]['battlefield']['after']->quantify($originalBattlefield);
}
usort($moves, function($a, $b) {
if ($a['quantify'] === $b['quantify']) return 0;
else return ($a['quantify'] > $b['quantify']) ? -1 : 1;
});
return $moves[0]['quantify'];
}
What this does is that it recursively checks future moves, until the $thinkAheadleft value is reached, OR until a solution was found (ie, all monsters were defeated). When it reaches it's exit parameter, it calculates the state of the battlefield, compared to the $originalBattlefield (the battlefield state before the first move). The calculation is made in the following way:
/** Quantify the current state of the battlefield
*
* #param Battlefield $originalBattlefield (the original battlefield)
*
* returns int (returns an integer with the battlefield quantification)
*/
public function quantify(Battlefield $originalBattlefield) {
$points = 0;
foreach ($originalBattlefield->Monsters as $originalMonsterId => $OriginalMonster) {
$CurrentMonster = $this->getMonster($originalMonsterId);
$monsterActivated = $CurrentMonster->getActivations() - $OriginalMonster->getActivations();
$points+=$monsterActivated*($this->quantifications['activations'] + $this->quantifications['activationsPenalty']);
if ($CurrentMonster->isDead()) $points+=$this->quantifications['monsterKilled']*$CurrentMonster->Priority;
else {
$enragePenalty = floor($this->quantifications['activations'] * (($CurrentMonster->Enrage['max'] - $CurrentMonster->Enrage['left'])/$CurrentMonster->Enrage['max']));
$points+=($OriginalMonster->Health['left'] - $CurrentMonster->Health['left']) * $this->quantifications['health'];
$points+=(($CurrentMonster->Enrage['max'] - $CurrentMonster->Enrage['left']))*$enragePenalty;
}
}
return $points;
}
When quantifying some things net positive points, some net negative points to the state. What the AI is doing, is, that instead of using the points calculated after his current move to decide which move to take, he uses the points calculated after the think ahead portion, and selecting a move based on the possible moves of the other heroes.
Basically, what the AI is doing, is saying that it isn't the best option at the moment, to attack Monster 1, but IF the other heroes will do this-and-this actions, in the long run, this will be the best outcome.
After selecting a move, the AI performs a single move with the hero, and then repeats the process for the next hero, calculating with +1 moves.
ISSUE: My issue is, that I was presuming, that an AI, that 'thinks ahead' 3-4 moves, should find a better solution than an AI that only performs the best possible move at the moment. But my test cases show differently, in some cases, an AI, that is not using the think ahead option, ie only plays the best possible move at the moment, beats an AI that is thinking ahead 1 single move. Sometimes, the AI that thinks ahead only 3 moves, beats an AI that thinks ahead 4 or 5 moves. Why is this happening? Is my presumption incorrect? If so, why is that? Am I using wrong numbers for weights? I was investigating this, and run a test, to automatically calculate the weights to use, with testing an interval of possible weights, and trying to use the best outcome (ie, the ones, which yield the least number of turns and/or the least number of activations), yet the problem I've described above, still persists with those weights also.
I am limited to a 5 move think ahead with the current version of my script, as with any larger think ahead number, the script gets REALLY slow (with 5 think ahead, it finds a solution in roughly 4 minutes, but with 6 think ahead, it didn't even find the first possible move in 6 hours)
HOW THE FIGHT WORKS: The fight works in the following way: a number of heroes (2-4) controlled by the AI, each having a number of different attacks (1-x), which can be used once or multiple times in a combat, are attacking a number of monsters (1-9). Based on the values of the attack, the monsters lose health, until they die. After each attack, the attacked monster gets enraged if he didn't die, and after each heroes performed a move, all monsters get enraged. When the monsters reach their enrage limit, they activate.
DISCLAIMER: I know that PHP is not the language to use for this kind of operation, but as this is only an in-house project, I've preferred to sacrifice speed, to be able to code this as fast as possible, in my native programming language.
UPDATE: The quantifications that we currently use look something like this:
$Battlefield->setQuantification(array(
'health' => 16,
'monsterKilled' => 86,
'activations' => -46,
'activationsPenalty' => -10
));
If there is randomness in your game, then anything can happen. Pointing that out since it's just not clear from the materials you have posted here.
If there is no randomness and the actors can see the full state of the game, then a longer look-ahead absolutely should perform better. When it does not, it is a clear indication that your evaluation function is providing incorrect estimates of the value of a state.
In looking at your code, the values of your quantifications are not listed and in your simulation it looks like you just have the same player make moves repeatedly without considering the possible actions of the other actors. You need to run a full simulation, step by step in order to produce accurate future states and you need to look at the value estimates of the varying states to see if you agree with them, and make adjustments to your quantifications accordingly.
An alternative way to frame the problem of estimating value is to explicitly predict your chances of winning the round as a percentage on a scale of 0.0 to 1.0 and then choose the move that gives you the highest chance of winning. Calculating the damage done and number of monsters killed so far doesn't tell you much about how much you have left to do in order to win the game.
I'm currently coding a game, and in one part of the code, it's checking to see if the player is close enough to a monster in mArray for its health bar to appear. I use hitTestObject for this with var i incrementing to go through the list. However, I will randomly get this error message:
TypeError: Error #2007: Parameter hitTestObject must be non-null.
After seeing this for the first time, I put the test all under the conditional if (mArray.length > 0) to make sure the Array() was filled before even attempting the test... but it still occurs at random times. I even used trace(i) to see if it happened on a certain part of the list, but it's completely random each debug session! I know the player portion can't be null since it is on the screen at all times during the hitTest, so I'm at a loss for words right now.
The portion of code in question (nothing else is related to this except for the use of mArray[i] in different places):
for (var i:int = 0; i < mArray.length; i++) {
if (mArray.length > 0) {
trace(i); //my attempt to see if it occurred at a specific incrementation of i
if (player.hitTestObject(mArray[i])) {
mArray[i].healthBar.visible = true;
} else {
mArray[i].healthBar.visible = false;
}
}
}
Again, it works perfectly for everything all of the time except for random cases it gives me the TypeError. Is this just a hiccup in my code, is there some obvious problem that I'm overlooking, or does the code just like to throwback to 2007?
EDIT: The debug session ALWAYS points to the line with if (player.hitTestObject(mArray[i])).
It looks like for some i the mArray[i] has a null value. Either make sure this won't happen when populating the array or just add extra check in hitTestObject line like this:
if (mArray[i] && player.hitTestObject(mArray[i])) { ... }
I don't know your game's architecture but maybe it would be more convenient to move the hitTestObject test into monster class instead of checking it externally. Typical approach is to have each entity to have some kind of "update" method that is executed once in every game loop.
I am using the "tick" event's delta property in EaselJS in order to create a simple timer in milliseconds. My ticker is set to 60 FPS. When the game is running I am getting roughly 16/17 ms between each tick (1000/60 = 16.6667) - so I am happy with this. However, when I append this value onto my text value (starting from 0) it is going up considerably quicker than it should be. I was expecting that on average it would be displaying a time of 1000 for each second elapsed. My code (in chunks) is below (game.js and gameInit.js are separate files). I am hoping that I am just overlooking something really simple...
//gameInit.js
createjs.Ticker.setFPS(60);
createjs.Ticker.on("tick", this.onTick, this);
...
//game.js
p.run = function (tickerEvent) {
if (this.gameStarted == true ) {
console.log("TICK ms since last tick = " + Math.floor(tickerEvent.delta)); // returns around 16/17
this.timerTextValue += Math.floor(tickerEvent.delta); //FIXME seems too fast!
this.timerText.text = this.timerTextValue;
}
};
Kind Regards,
Rich
Solved it. What a silly mistake! So, I had another place where I was initialising the ticker meaning it was being invoked twice, hence the reason that my timer was displaying doubly quick
I am very new to programming and coming from a "custom map" background in games like SC2. I am currently trying to make a platformer game in Love2d. But I wonder how I can make something wait X amount of seconds before doing the next thing.
Say I want to make the protagonist immortal for 5 seconds, how should that code look like ?
Immortal = true
????????????????
Immortal = false
As I have understood there is no built in wait in Lua nor Love2d.
It sounds like you're interested in a temporary state for one of your game entities. This is pretty common - a powerup lasts for six seconds, an enemy is stunned for two seconds, your character looks different while jumping, etc. A temporary state is different than waiting. Waiting suggests that absolutely nothing else happens during your five seconds of immortality. It sounds like you want the game to continue as normal, but with an immortal protagonist for five seconds.
Consider using a "time remaining" variable versus a boolean to represent temporary states. For example:
local protagonist = {
-- This is the amount of immortality remaining, in seconds
immortalityRemaining = 0,
health = 100
}
-- Then, imagine grabbing an immortality powerup somewhere in the game.
-- Simply set immortalityRemaining to the desired length of immortality.
function protagonistGrabbedImmortalityPowerup()
protagonist.immortalityRemaining = 5
end
-- You then shave off a little bit of the remaining time during each love.update
-- Remember, dt is the time passed since the last update.
function love.update(dt)
protagonist.immortalityRemaining = protagonist.immortalityRemaining - dt
end
-- When resolving damage to your protagonist, consider immortalityRemaining
function applyDamageToProtagonist(damage)
if protagonist.immortalityRemaining <= 0 then
protagonist.health = protagonist.health - damage
end
end
Be careful with concepts like wait and timer. They typically refer to managing threads. In a game with many moving parts, it's often easier and more predictable to manage things without threads. When possible, treat your game like a giant state machine versus synchronizing work between threads. If threads are absolutely necessary, Löve does offer them in its love.thread module.
I normally use cron.lua for what you're talking about: https://github.com/kikito/cron.lua
Immortal = true
immortalTimer = cron.after(5, function()
Immortal = false
end)
and then just stick immortalTimer:update(dt) in your love.update.
You could do this:
function delay_s(delay)
delay = delay or 1
local time_to = os.time() + delay
while os.time() < time_to do end
end
Then you can just do:
Immortal == true
delay_s(5)
Immortal == false
Of course, it'll prevent you from doing anything else unless you run it in its own thread. But this is strictly Lua as I know nothing of Love2d, unfortunately.
I reccomend that you use hump.timer in your game,like this:
function love.load()
timer=require'hump.timer'
Immortal=true
timer.after(1,function()
Immortal=false
end)
end
instead of using timer.after,you can also use timer.script,like this:
function love.load
timer=require'hump.timer'
timer.script(function(wait)
Immortal=true
wait(5)
Immortal=false
end)
end
don't forget to add timer.updateinto function love.update!
function love.update(dt)
timer.update(dt)
end
hope this helped ;)
Download link:https://github.com/vrld/hump
Sorry, but this one is what some may call an open-ended question.
I am attempting to make a command where if someone says !spectate, they are put in spectate mode, and stay in there for 30 seconds. So far I have been totally unsuccessful, and as such, have no idea really of what I'm doing, so not erroneous code available :(
Here's some things which may assist those who answer:
To put one in spectate mode, after someone says !spectate (without the 30 second limit) you do this:
if Message == "!spectate" then
InputConsole("spectate %d", pID)
end
The game this will be used with is Command and Conquer: Renegade
Sorry I can't be much more helpful than that, I am totally out of my depth here!
However you'd go about doing this would be specific to Renegade's Lua API. I've never used it myself, but the almighty Google reckons that Renegades uses LuaTT, for which the API docs say:
You can have only 255 scripts attached to objects. For timers, make your own api based of OnThink and os.time
The docs aren't particularly good, but a quick look at the example code found here suggests something along these lines would work:
local timers = {
{ time = 1343910384, cb = function() doSomething() end }
}
function OnThink() -- this is called every frame
for i = 1, #timers do
if os.time() > timers[i].time then
timers[i].cb()
table.remove(timers, i)
end
end
end
The code you posted would then look something like this:
if Message == "!spectate" then
InputConsole("spectate %d", pID) -- move player to spectators
table.insert(timers, {
time = os.time() + 30, -- 30 seconds from now,
cb = function() InputConsole("spectate %d", pID) end -- remove player from spectators
})
end