How would I add a sound effect every time a player enters a new level? I'm trying to figure this out for a project that is due tonight and I've been working on it for weeks and my project uses differ classes for different aspects of the project.
Level class code:
package {
public class LevelData {
public var backgroundImage:String;
public var pointsToReachNextLevel:Number;
public var enemySpawnRate:Number;
public var levelNum:Number;
public function LevelData(levelNumber:Number) {
levelNum = levelNumber;
if ( levelNumber % 2 == 1 )
{
backgroundImage = "Cosmic";
pointsToReachNextLevel = 150;
enemySpawnRate = 0.05;
}
else if ( levelNumber == 2 )
{
backgroundImage = "Mystic";
pointsToReachNextLevel = 350;
enemySpawnRate = 0.1;
}
else if ( levelNumber == 3 )
{
backgroundImage = "Cosmic";
pointsToReachNextLevel = 600;
enemySpawnRate = 0.15;
}
else if ( levelNumber == 4 )
{
backgroundImage = "Mystic";
pointsToReachNextLevel = 5820; // roughly 3 minutes enemies stop appearing
enemySpawnRate = 0.6 - ( 2 / levelNumber );
}
}
}
}
snippet from Engine class:
public function setBackgroundImage(): void //background change
{
if (currentLevelData.backgroundImage == "Cosmic") {
backgroundContainer.addChild(new CosmicBackground());
} else if (currentLevelData.backgroundImage == "Mystic")
{
backgroundContainer.addChild(new MysticBackground());
}
}
There are multiple ways of playing sounds in Flash. Honestly, the most reliable approach for use with code is to write a dedicated class for yourself, implementing SoundChannel, that manages all sound loading, playback, and transformation (volume, pan, etc.) It takes a bit of work, but it pays off in the long run.
Of course, it would seem you're on a tighter deadline than that. If you need a quick-and-dirty fix for this, here it is.
Add your sound to the Flash library. (This should speed things up, otherwise you have to load it from an external file constantly.)
Set up the sound in your library to be accessible from ActionScript 3. Here's an answer detailing how to do that. For this, I'll call it "sndLevelUp."
Create an instance of your sound at the top of your class: var levelUp:sndLevelUp = new sndLevelUp();
Play the sound every time the player levels up. levelUp.play();
If you have more time, you may want to set up a SoundChannel, which gives you more control over playback, volume, and panning. Otherwise, this is the fast way to do it.
Related
My app is very slow on mobile devices.
It uses a lot of Event.ENTER_FRAME event listeners, so I switched (as I read they are much more performance heavy) to a global timer that these classes listen for if they need to update (they only use these timers if they have been initiated by a TouchEvent).
I also tried enabling hardware acceleration, and switching to using the CPU, but these have not helped with the lag issues to a noticable amount. (my app has very few animations so I think this is the best decision)
I do have a lot of MovieClips in it, however very few of them actually animate, so I doubt this is an issue. (This is also why I do not freeze and unfreeze objects, as they are never removed from the stage)
On this website they said the following about timers
– the more timer instances linger in the system, the more you can expect slow and choppy gameplay or unresponsive UI.
I only use one timer, so I didn't think this would be an issue, but I do have a lot of event listeners that use this timer to update, so I can only figure that the timer must be the issue. Thus to avoid lag I am looking for a better alternative.
To be clear, I'm not using many Event.ENTER_FRAME event listeners anymore, I've mostly switched to timers. With that being said, here is an example of a more resource demanding one.
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class mc_BattleScene extends MovieClip
{
public static var mc_PlayerFace:MovieClip;
public static var enemyAttackEffect:MovieClip;
public static var mc_playerHitBox:MovieClip;
public static var battler1:Number = 1;
public static var battler2:Number = 1;
public static var battler3:Number = 1;
public var lvlModifier:Number;
public var dmgReduction:Number;
public var dmgDealt:Number;
public static var dmgSpeed:Number = 1;
public function mc_BattleScene()
{
visible = false;
addEventListener(Event.ENTER_FRAME, onFrame);
mc_PlayerFace = playerFace_mc;
enemyAttackEffect = attackEffect_mc;
mc_playerHitBox = playerHitBox_mc;
function onFrame(e:Event):void
{
battler1_mc.gotoAndStop(battler1);
battler2_mc.gotoAndStop(battler2);
battler3_mc.gotoAndStop(battler3);
if (Game.playerInteractionStatus[1])
{
//we are fighting
visible = true;
if (attackEffect_mc.hitTestObject(playerHitBox_mc))
{
// attack hit us, deal dmg
dmgReduction = (Game.playerStats[2] * (Game.enemyStats[1]));
dmgDealt = Game.enemyStats[1] - dmgReduction;
attackEffect_mc.x = 516;
if (Game.playerStats[0] - Math.round(dmgDealt) <= 0)
{
// round dmg to 0 (were dead)
Game.playerStats[0] = 0;
}
else
{
// deal damage to us
Game.playerStats[0] -= Math.round(dmgDealt);
}
}
else if(attackEffect_mc.hitTestObject(wall))
{
//stop the player from moving (by doing nothing)
}
else
{
attackEffect_mc.x -= dmgSpeed;
}
}
else
{
// reset the position of the attack effect if we are not fighting
visible = false;
attackEffect_mc.x = 516;
}
}
}
}
}
This example of Event.ENTER_FRAME is one of the few that actually are this complicated, the other Event.ENTER_FRAMEs tend to simply update values (ex: update a text field to display correct number variables)
For example: creating multiple vars inside an enterframe could clog
the memory and cause lag.
I've avoided doing stuff like this, as not only what you've said, but in my opinion I think it doesn't make sense to continuously define it in a function, I update these values only.
Resources
http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-8000.html
max links allowed
I am building an Adobe Air AS3 IOS and Android App, in which i have a movie clip in the center of the stage. When you start touching this movie clip, you can move it all around the stage.
This is how i'm doing so :
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
MC_M1.alpha = 1;
MC_M1.addEventListener(Event.ENTER_FRAME, ifHitAct);
MC_M1.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
MC_M1.x = 0.516 * gameIntro.stageWidthToUse;
MC_M1.y = 0.75 * gameIntro.stageHeightToUse;
MC_M1.height = 0.2 * gameIntro.stageHeightToUse;
MC_M1.width = MC_M1.height / 1.4;
gameIntro.STAGE.stage.addChildAt(MC_M1,1);
function onTouchBegin(event:TouchEvent)
{
trace("TouchBegin");
if (touchMoveID != 0)
{
trace("It Did Not");
return;
}
touchMoveID = event.touchPointID;
gameIntro.STAGE.stage.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
gameIntro.STAGE.stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
}
function onTouchMove(event:TouchEvent)
{
if (event.touchPointID != touchMoveID)
{
return;
}
//trace("Moving")
MC_M1.x = event.stageX;
MC_M1.y = event.stageY;
}
function onTouchEnd(event:TouchEvent)
{
if (event.touchPointID != touchMoveID)
{
return;
}
//trace("Ending");
touchMoveID = 0;
gameIntro.STAGE.stage.removeEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
gameIntro.STAGE.stage.removeEventListener(TouchEvent.TOUCH_END, onTouchEnd);
}
When the player actually looses the game, what i am actually doing is the following :
MC_M1.removeEventListener(Event.ENTER_FRAME , ifHitAct);
MC_M1.removeEventListener(TouchEvent.TOUCH_BEGIN , onTouchBegin);
gameIntro.STAGE.stage.removeChild(MC_M1);
MC_M1.alpha = 0;
isDead = 1;
replayButToUse.x = 0.127 * gameIntro.stageWidthToUse;
replayButToUse.y = 0.91 * gameIntro.stageHeightToUse;
replayButToUse.addEventListener(MouseEvent.CLICK, gotoIntro);
This is all happening in a class called : introClassToUse.
So when the users looses, he will get a replay button, and when he clicks it, he will go back to the same class and reload everything, using the following code :
function gotoIntro(event:MouseEvent):void
{
replayButToUse.removeEventListener(MouseEvent.CLICK, gotoIntro);
replayButToUse.alpha = 0;
replayButToUse.removeEventListener(MouseEvent.CLICK, gotoIntro);
stop();
var reload:introClassToUse = new introClassToUse();
}
And so everything loads back up and the game restarts. My problem is, i'm facing a very weird behavior when i tend to replay the game more than 2-3 times. The MC_M1 just stops listening to any touch event, but keeps on listening to ENTER_FRAME events, in which i keep touching the MC_M1 but it seems to not respond to it. I even debugged it remotely from my iPhone, for the first couple of replays, i can see the trace("TouchBegin"); with it's outcome, it was showing me TouchBegin, but after a few replays, the touch events just froze. What am i missing?
Any help is really appreciated, i'm new in AS3, i need to learn so i could manage more
Edit 1 :
I have no code on any frame, i just have lots of AS Classes.
The fla file is linked to an AS Class called gameIntro. In this class, i have linked the following :
- STAGE is an object of type Stage.
- gameIntro.STAGE = stage
Later on, when the user clicks a play button, i call the class introClassToUse. This class has all the game functionalities. All the code present above is in introClassToUse. When the user looses and clicks the replay button, he will go to "goToIntro" function, im which i recall the introClassToUse.
It's all working fine, with several other timers implemented and all, the only problem is that after several replays, the MC_M1 just freezes over
I am removing the MC_M1 each time the user looses and re-add them when i call back the introClassToUse, because i tried to use the .visible property, it didn't work at all ( this is why i am using the gameIntro.STAGE.stage.removeChild(MC_M1)
I know the question is old but maybe someone is still wondering what is going on here (like me).
There are lot of problems in you code but I thing the root of your problem starts here:
function gotoIntro(event:MouseEvent):void{
//...
var reload:introClassToUse = new introClassToUse();
}
It is usually unwanted behavior if simply creating an instance does more than nothing to your program and you don't even need to assign it to variable in this case.
You mentioned this code is located in your introClassToUse class. This basically means that you are creating new instance of your game inside old one and this seem to be completely awry.
You should consider using only instance properties in your class definition and create new introClassToUse() in external classes;
You didn't include many important details about your code like
How the whole class structures look like - for example you can't place line like MC_M1.addEventListener(Event.ENTER_FRAME, ifHitAct);in the scope of your class so obviously you have this in some function and we don't know when and from where it is called.
Where and how your variables are declared, and assigned. It's hard to tell if your MC_M1 is property of an instance or a class, is it internal/public/private/...
Do you link library symbols to your classes or acquire it from stage.
There could be many things that could give you such result. Based on what you wrote I've reproduced behavior similar to what you've describe but using mouse event and a dummy loose condition. This ends the game each time you drop the mc partially outside right edge of the sage, show restart button and starts again if you click it (basically it's mostly your code). It works fine for about 10s and than suddely you can't move the mc anymore. The frame event is still tracing out but touch/mouse is not.
How can it be? I suspect that you could remove only listeners somewhere and have invisible mc stuck on the new one. And this could be easy overlooked, especially if you using static properties. Again we don't even know where is your movie clip coming from so we can only guess what is happening whit your code but I've tried to take the example simple this is how I did it. The problem may lay in some completely different place but you can guess for all scenarios.
Document class of the project - GameIntro.as
package
{
import flash.display.Sprite;
public class GameIntro extends Sprite
{
//Document class. this need to be compiled with strict mode off.
public function GameIntro() {
GameIntro.STAGE = stage;
GameIntro.stageWidthToUse = stage.stageWidth;
GameIntro.stageHeightToUse = stage.stageHeight;
var intro:IntroClassToUse = new IntroClassToUse();
stage.addChild(intro);
}
}
}
IntroClassToUse.as
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
* You need to have library symbol linked to this class in .fla with two mcs -
* mcFromLibrarySymbol (dragable) and repButton (reapatButton)
*/
public class IntroClassToUse extends MovieClip
{
var t = 0; //timer ticks
var fc:uint = 0; //frames counter
var isDead = 0;
var mc;
static var repButton;
var logicContex:Timer = new Timer(30);
public function IntroClassToUse() {
trace("toUse", GameIntro.stageWidthToUse);
mc = mcFromLibrarySymbol;
if(!repButton) repButton = repButtonX;
logicContex.addEventListener(TimerEvent.TIMER, logicInterval);
logicContex.start();
init();
}
internal function init() {
trace("init");
mc.alpha = 1;
mc.addEventListener(Event.ENTER_FRAME, onFrame);
mc.addEventListener(MouseEvent.MOUSE_DOWN, onMDown);
mc.x = 0.516 * GameIntro.stageWidthToUse;
mc.y = 0.75 * GameIntro.stageHeightToUse;
mc.height = 0.2 * GameIntro.stageHeightToUse;
mc.width = mc.height / 1.4;
GameIntro.STAGE.stage.addChildAt(mc, 1);
}
internal function onLoose() {
trace("onLoose");
mc.removeEventListener(Event.ENTER_FRAME , onFrame);
mc.removeEventListener(MouseEvent.MOUSE_DOWN, onMDown);
GameIntro.STAGE.stage.removeChild(mc);
mc.alpha = 0;
isDead = 1;
repButton.x = 0.127 * GameIntro.stageWidthToUse;
repButton.y = 0.91 * GameIntro.stageHeightToUse;
repButton.addEventListener(MouseEvent.CLICK, onReplay);
repButton.alpha = 1;
}
internal function onReplay(e:MouseEvent):void {
trace("onReplay");
repButton.removeEventListener(MouseEvent.CLICK, onReplay);
repButton.alpha = 0;
stop();
new IntroClassToUse();
}
internal function onMDown(e:MouseEvent):void {
trace("mouseDow");
GameIntro.STAGE.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMMove);
GameIntro.STAGE.stage.addEventListener(MouseEvent.MOUSE_UP, onMUp);
}
internal function onMMove(e:MouseEvent):void {
mc.x = e.stageX;
mc.y = e.stageY;
}
//you loose the game if you release you mc with part of it over rigth stage edge.
internal function onMUp(e:MouseEvent):void {
trace("mouseUp");
GameIntro.STAGE.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMMove);
GameIntro.STAGE.stage.removeEventListener(MouseEvent.MOUSE_UP, onMUp);
trace("Stage:", GameIntro.STAGE.numChildren);
if (mc.x + mc.width > GameIntro.STAGE.stageWidth) onLoose();
}
internal function onFrame(e:Event):void {
trace("frames", fc++);
}
internal function logicInterval(e:TimerEvent):void {
if (t++ < 300 || !isDead) return;
init();
mc.alpha = 0;
mc.removeEventListener(MouseEvent.MOUSE_DOWN, onMDown);
isDead = 0;
}
}
}
This current code when it is the upright state it runs the bass sound repeatedly until it starts distorting the sound and running poorly. How would i dispose of the sound when it has finished running and stopping it run multiple times as i would like the sound only to play once when it has gone into this upright state. Thanks alex.
public void determineState(){
accelX = Gdx.input.getAccelerometerX();
accelY = Gdx.input.getAccelerometerY();
accelZ = Gdx.input.getAccelerometerZ();
state = "NULL";
if(accelX < 1 && accelX > -1){
if(accelY > -1 && accelY < 1){
if(accelZ > 9 && accelZ < 11){
state = "UPRIGHT";
}
}
}
}
public void playSound(String soundString){
System.out.println(soundString);
if(soundString != "null"){
sound = Gdx.audio.newSound(Gdx.files.internal(soundString + ".wav"));
long id = sound.play();
sound.setVolume(id, 3);
}else if (soundString == "null"){
}
}
You should not create a new sound and dispose it every time you want it to play. This is wasting time reloading it over and over. Store your loaded sounds in an array, or load them with an AssetManager and get the references from there.
Your playSound method would look something like this:
public void playSound (String soundFileName){
sound = assetManager.get(soundFileName, Sound.class);
sound.play();
}
By the way, it doesn't make sense to set volume to 3 since it's on a 0-1 scale.
If you want something to happen only one time when a state changes, than call that method when the state changes, not on every frame as it seems you are doing.
i.e. do this:
//...
if(accelZ > 9 && accelZ < 11){
state = "UPRIGHT";
playSound("uprightStateChange.wav");
}
I am a total noob at AS3, roughly 1 year experience so please be lenient with me :)
I currently am making an endless runner game and I'm making the obstacles spawn using this method
var therespawn:RespawnObject;
var thecone:trafficcone;
var started:Boolean = false;
var dx:Number = 10;
var dy:Number = 10;
stage.addEventListener(Event.ENTER_FRAME, startGame);
addEventListener(Event.ENTER_FRAME, collision);
addEventListener(Event.ENTER_FRAME, coneCollision);
function startGame(evt:Event):void {
if (started == false) {
spawnHazard();
}
}
function spawnHazard() {
started = true;
therespawn = new RespawnObject();
addChild(therespawn);
thecone = new trafficcone();
addChild(thecone);
therespawn.x = -50;
therespawn.y = 310;
thecone.x = 600;
thecone.y = 310;
}
function collision(evt:Event):void {
thecone.x -= 15;
if(thecone.hitTestObject(therespawn)) {
thecone.x = 600;
}
}
Now the only way to finish the game or end it is to get hit by an obstacle which ive shown down below:
function coneCollision(evt:Event):void {
if(MainChar.hitTestObject(thecone)) {
gotoAndStop("frameFive");
}
}
Everytime the highscore frame appears the cone is still spawning and despawning, why is that?
I haven't declared them as global?
Any help appreciated, thanks!
You can fix your problem by setting started to false:
function coneCollision(evt:Event):void {
if(MainChar.hitTestObject(thecone)) {
started = false;
gotoAndStop("frameFive");
}
}
The flash timeline really only has to with the way MovieClips are visually displayed as children of the stage. Removing an object from the timeline doesn't just suddenly nullify all the code associated with that object. In other words, your ENTER_FRAME method still runs in the background even if the object is no longer a child of the Stage, regardless of the frame number for the MovieClip. If you're serious about coding you might consider investigating in Classes and Object Oriented AS3. Classes are much nicer to work with than the Flash timeline.
So I have been making this game with Action Script 3, and CS5.5. What you are trying to do is avoid asteroids while you fly through space. I thought it would be cool to have the planets in out solar system be moving down the screen throughout the game in the background. Kinda to make it look like you were flying pass them. The way I did this was I added five to their y coordinate each frame per second. Once their y coordinate reached 600 ( the bottom of the screen ) I would add a new planet which would do the same thing. For some reason once I got to Saturn everything got weird. Saturn came to early, and so did Uranus. I had no idea what was going on. I have been frustrated with this for a good hour. Here is the part where I think there is the problem.
public function onTick(timerEvent:TimerEvent):void
{
earth.PlanetMovement(5);
if (earth.y==600)
{
mars.PlanetsStart(300, -100);
addChild( mars );
levels=levels+5;
}
mars.PlanetMovement(5);
if (mars.y==600)
{
jupiter.PlanetsStart(300,-150);
addChild (jupiter);
levels=levels+10;
}
jupiter.PlanetMovement(5);
if (jupiter.y==600)
{
saturn.PlanetsStart(300,-155);
addChild (saturn);
levels=levels+20;
}
saturn.PlanetMovement(5);
if (saturn.y==600)
{
uranus.PlanetsStart(300,-160)
addChild ( uranus);
levels=levels+25;
}
uranus.PlanetMovement(5);
PlanetMovement, and PlanetsStart are two functions in the Planets class. If you need more info please tell me.
EDIT: I guess I should explain further. PlanetsStart is a function that has the starting coordinate of each movieclip. So once earth reached a y coordinate of 600, then mars starts at (300, -100). Then it is added to the screen. levels is a variable that raises the score each fps. PlanetMovement is how much each movieclip will move each fps. If I were to use >= then the score would raise too much.
This is exactly what happens. earth shows up where it is supposed to. Then mars showd up on time. Then for some reason Saturn pops up in the middle of mars, and Jupiter. After this Saturn reaches the bottom making Uranus appear. Then Jupiter reaches the bottom, and everything works as it should. Saturn shows up, and then Uranus in order
Ugh. Unfortunately, this is more complex than you are implying. It would really help you to write a code plan of some sort first, develop some psuedo-code that will show basic logic (and the logic errors), and then code after that.
Below is an example of a better way to structure this idea. However, I don't think your idea is terrible memory conscious. You should only bring in the planets as needed, and remove them as needed as well. Look into a technique called "object pooling" to help better structure this.
Solar system class:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class SolarSystem extends MovieClip
{
private var PLANET_NAMES:Array = new Array("earth", "mars", "jupiter", "saturn", "uranus");
// you will have to fix these values:
private var PLANET_STARTS:Array = new Array(new Point(300, -100), new Point(300, -100),new Point(300, -100),new Point(300, -100),new Point(300, -100));
private var planets:Array;
private var maxY:Number = 600;
private var speed:Number = 5;
private var levels:Number = 0;
private var levelIncrement:Number = 5;
public function SolarSystem()
{
super();
addEventListener(Event.ADDED_TO_STAGE, initHnd, false, 0, true);
}
private function initHnd(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initHnd);
runPlanets();
addEventListener(Event.ENTER_FRAME, frameHnd, false, 0, true);
}
private function runPlanets():void
{
for (var i:int = 0; i < PLANET_NAMES.length; i++)
{
planets[i] = new Planet();
planets[i].name = PLANET_NAMES[i];
Planet(planets[i]).startPlanet(PLANET_STARTS[i]);
this.addChild(planets[i]);
}
}
private function frameHnd(e:Event):void
{
if(planets && planets.length > 0){
// move all the planets until they are too low, then remove them.
// decrementing loop because planets will be being removed occasionally.
for (var i:int = planets.length -1; i >= 0; i--)
{
if (planets[i] && Planet(planets[i]).y >= maxY) {
// this seems very strange to me, but it will replicate what your code says:
levels += (levels + levelIncrement);
try {
removeChild(Planet(planets[i]));
planets[i] = null;
}catch (e:Error) { }
} else if ( Planet(planets[i]).isMoving ){
Planet(planets[i]).movePlanet(0, speed);
}
}
} else {
removeEventListener(Event.ENTER_FRAME, frameHnd);
}
}
}
}
and here is the planet class:
package
{
import flash.display.MovieClip;
public class Planet extends MovieClip
{
private var _isMoving:Boolean = false;
public function Planet()
{
super();
}
public function startPlanet(sx:Number, sy:Object):void
{
this.x = sx;
this.y = sy;
isMoving = true;
}
public function movePlanet(dx:Number, dy:Number):void
{
if (isMoving)
{
this.x += dx;
this.y += dy;
}
}
public function get isMoving():Boolean
{
return _isMoving;
}
public function set isMoving(value:Boolean):void
{
_isMoving = value;
}
}
}
Again, this is not the best way to do this, but it is more manageable to group concepts and actions into classes.
HTH.
Right off the bat I can see a major issue. Change all the statements from:
if (saturn.y==600)
to
if (saturn.y>=600)
Just saying == means that you're only going to trigger the conditional in the case that your Y value is exactly 600.
Note you can also change stuff like levels = levels+20 to simply levels += 20;.
When moving stuff around like this it is a good idea to check the position by using ">=" instead of "==". What if your object starts at y=1? Then it will never reach 600 it will just keep on going down forever.
Also you seem to be calling PlanentMovement even when PlanetsStart hasn't been called yet.