Ok, so I'm working on a quiz game for work. I have each question on its own movie clip (I know that's not the preferred method, but it's what I'm most familiar with and a compressed project timeline kinda forced my hand).
Because players will only be able to play the game once (there's a prize involved), I'm attempting to implement a "save/load" system which will allow players to pickup where they left off, in case their browser crashes, they accidentally close the game window, etc. I already have a SO setup to accept save data. So for example, I have the following code on frame 1 of the Q1 scene:
var currentLevel = "Q1"; //first time the var is defined. "Q1" is the name of the scene
answersSO.data.currentLevel = currentLevel;
answersSO.flush();
And I have the following code on frame 1 of the very first scene of the movie:
function checkProgress() {
if(answersSO.data.currentLevel != undefined) {
currentLevel = answersSO.data.currentLevel;
MovieClip(this.root).gotoAndPlay(1, currentLevel);
} else {
gotoAndPlay(2);
}
}
checkProgress();
First, I gotta say that I'm a real nub at AS. I test play the game up to the Q1 scene, then close the window. I restart the game and it instantly skips all of scene 1 and proceeds to scene 2 (which is an intro to the game, not Q1). No error is thrown.
What I'm looking to do is save some data to the currentLevel variable, and save that variable to the SO. At the start of the game, the script should check if the SO has any data in it. If not, then there is no save data—proceed as normal. If there is data, load it and play the last recorded scene the player was at.
I can't figure out (1) how to get this working and (2), of somewhat less importance, why no error is being thrown.
EDIT!
This is my updated code:
stop();
import flash.net.SharedObject;
var answersSO:SharedObject = SharedObject.getLocal("PWFGame"); //DECLARE SO VARIABLE
var currentProgress = "";
//CHECK SO FOR PROGRESS
checkProgress();
function checkProgress() {
if(answersSO.data.currentLevel == null) {
nameField.text = "Enter Name Here";
gotoAndPlay(2);
} else {
currentProgress = answersSO.data.currentLevel;
MovieClip(this.root).gotoAndPlay(1, currentProgress);
}
}
It does work, but the last line in the else statement is acting strangely. It skips over the first scene completely and plays the next scene from frame 1 (which is called "Intro"). Even if I change currentProgress to "Q1" it does the same thing. Why would this line of code work elsewhere and not here?
SharedObjects can be deleted by the user. A player could delete that file and start again.
If there is a login name, you should store that data on the server using a server side script (php, asp, etc.)
So add a check at the beginning and let the user play just if his name has not been stored on the server. Than call this list form flash at the beginning of the game.
For the browser crash, you can store the data only when the game is ended. So when you refresh the page you can start it again.
If there is no username, you should get other data, but it isn't so simple as an IP could change and more important data can't be retrieved by the flash player (you can do it with a desktop application made with AIR).
At the and...
.gotoAndPlay(1, currentLevel);
Remeber that the first value is the scene and the second is the frame.
Nadia
Ok, after much digging, I've discovered that gotoAndPlay() on frame 1 can cause issues. I moved the updated code block (seen above) to frame 2, and everything works.
Related
I am currently on an adventure with my sanity at stake where I try to simply play an mp3 file on my web-application. This mp3 is loaded chunk-wise in order to make the experience more responsive.
Even though the documentation of the MediaSource API is just remarkably, almost violently, useful and hundreds of well explained examples pave the way towards salvation, I find myself not able to make everything work properly.
The last issue I am facing is the seeking-functionality. The simple idea I had for this feature is to simply kill everything that's alive, burn everything down and revive again. To be more precise, if the user seeks, I stop the machinery and start playing everything from scratch, but with the desired offset (in seconds).
Everything is working so far except one last thing: currentTime, gets set to a different value after I have set it to the time that the user requested.
The track continues playing as expected but the problem is that currentTime is what's being used to visualize the current location within the track for the user.
This is how the events take place before and after the user seeks
[play-click] <--- User clicks play button
:
[timeupdate] currentTime: 00.01
[timeupdate] currentTime: 00.02
[timeupdate] currentTime: 00.03
[seek-click] <--- User seeks, stop() is called and then playFrom(seconds)
[loadstart] currentTime: 33.33 # This is the (correct) time set in playFrom(seconds)
[loadmetadata] currentTime: 10.12 # Now the time gets set to this (wrong) value
[seek] currentTime: 10.12 # The triggered seek event comes in
[seeked] currentTime: 10.12
[canplay] currentTime: 10.12
[playing] currentTime: 10.12
[timeupdate] currentTime: 10.13 # Track continues to play correctly but this value is still wrong
:
This is how seeking takes place:
public play(track: Track) {
this.track = track;
this.playFrom(0);
}
public seekTo(seconds: number) {
this.stop();
this.playFrom(seconds);
}
public stop() {
this.logger.debug('Stop ..');
this.sourceBuffer.abort();
this.audioObj.pause();
this.removeEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);
this.audioObj = null;
this.sourceBuffer = null;
this.mediaSource = null;
}
Now, the playFrom(seconds) method is a bit more complicated than that but all it really does is re-create everything that I just destroyed:
private playFrom(seconds: number) {
this.logger.debug(`Start playing from ${seconds.toFixed(2)} seconds`)
// [:] (collapsed some less important code here)
// Create the media source object
this.mediaSource = new MediaSource();
this.mediaSource.addEventListener('sourceopen', this.onSourceOpen);
// Create audio element
this.audioObj = document.createElement('audio');
this.audioObj.src = URL.createObjectURL(this.mediaSource);
this.audioObj.currentTime = seconds;
this.addEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);
this.logger.debug(`Object-url: ${this.audioObj.src}`);
this.state = {currentTime: seconds, playing: true};
}
How can I get the correct current-time here?
I tried another idea where I was tring to create and add a new SourceBuffer to the MediaSource and let it play the audio from there but I wasn't able to do that either.
What is the duration reported by the MediaSource (or by the media element) at the time of issuing the seek to the media element? If it is infinite, you may need to use the {set,clear}LiveSeekableRange APIs in MSE to let the implementation know if you wish to let the player seek beyond the currently buffered media's highest presentation end time. Alternatively, you could possibly set the MediaSource duration before seeking to be something finite, but at least as high as the highest currently buffered media presentation end time.
First, sorry if my english is bad and I'm new to Actionscript 3.
I have a movieclip, I give it an instance name "symbol1" which include a code inside it, like this :
var a: int = 2;
var b: int = 3;
var total: int = a + b;
How can I access the "total" integer variable inside that movieclip in the main timeline? I try to trace it but the result is 0. In the main timeline I wrote this code :
trace(symbol1.total);
I appreciate the help, thanks
The "problem" is the order of execution of frames. The parent is executed first.
The value is 0 and not undefined because the property does indeed exist since the creation of the object which happens in a previous separate step.
You should be able to verify that by stepping through your code with the debugger.
I've made 6 games. each game is in the MovieClip including the game functions, times, scores, variables, etc. I want the total score of each game summed outside each MoviClips, which is in the main timeline. is there a solution to that problem?
This makes your timing problem even more apparent.
You arrive at a frame, which contains a game and you immediately want to access the total score of it. It should be obvious that the game first has to be played in order to obtain a score from it.
Asking a newborn baby what his favourite moment in the first 60 years of his live was doesn't make much sense.
You have to notify the main timeline that the game was finished. The best way to do this is to dispatch an Event from the game. Frames are totally unnecessary.
Your code could look something like this:
var game:Game = new Tetris();
addChild(game);
game.addEventListener(GameEvent.FINISH, onFinish);
function onFinish(e:GameEvent):void
{
trace("finished game with score " + game.score);
}
If you want to update the exterior score sum while the game is running (to immediately reflect any change in the score, not just when the game is finished), your best bet is to create a ScoreModel for the score which you pass to the game, the Game. The Game modifies the ScoreModel, which in turn causes it to dispatch Events:
var score:ScoreModel = new ScoreModel();
var game:Game = new Tetris(score);
addChild(game);
score.addEventListener(Event.CHANGE, onScoreChange);
function onScoreChange(e:Event):void
{
trace("score changed to " + score);
}
Both examples are "what your code could look like"-type of examples. There are many questions about how to dispatch events that explain how to do this in more detail.
Something that came up in the comments from #Jezzamon:
I'm pretty sure you're accessing the variable total correctly, otherwise I would expect it to cause an error (you can test this by trying to run trace(symbol1.variableNameThatDoesntExist)).
No, there cannot be an error for accessing a variable as seen in the question. The reason for that is that the property is accessed on a MovieClip object, which is a dynamic class. As such, the compiler doesn't know if accessing a particular property is valid, because that can change at runtime. And because nothing else relies on the value to be valid, there isn't a runtime error either.
Here's some example code to illustrate this:
trace("toString: " + {}.toString); //toString: function Function() {}
trace("bananas: " + {}.bananas); //bananas: undefined
This is one of the reasons why using MovieClip can be a bad idea. In addition to that, the Flash IDE modifies the code behind the scenes. That can lead to unexpected execution of code. A purely code based workflow sure is advantageous in this regard and recommended.
I have a multi user application (chat) (Flash CS6 AS3) where the userlist has more than 300 users on peak hours, I have noticed the SWF becomes slower as the userlist updates and is removing and adding more than 300 users. so my question is, is there a way just to update the list component by removing the user that just disconnected or adding the one that connected without re-populating the whole list? Thank you!
This is the current code:
//The function gets called everytime a SYNC is sent from server (FMS)
function syncEventHandler(event:SyncEvent)
{
list1.removeAll();
for (var i in users_so.data)
{
if (users_so.data[i] != null)
{
var clientObj = users_so.data[i];
list1.addItem({label:clientObj.UserName});
}
}
}
You could try storing your user list in an ArrayCollection (which has .addItAt() and .removeItemAt() methods that can be used in concert with .getItemIndex() to manage members of the underlying array)
I've looked everywhere for this answer..but I am not quite sure on how to do it exactly...
I am making a Flash Game using Flash AS3...
I need options where user can save and load their progress..
can somebody give a step by step on how to do this please? I am very new to actionscript...
usually other tuts shows u how to save certain variables
Cookies!
Lucky us it's very simple. Unlucky us it's not obvious how it's done:
// have that referenced all the time:
var cookies: SharedObject = SharedObject.getLocal("myGameData");
// start saving
cookies.data.progress = 50;
cookies.data.lives = 5;
cookies.data.anyRandomVariable = "my name or something";
cookies.flush();
// now it's saved
// start loading
var progress: Number = cookies.data.progress;
var lives: int = cookies.data.lives = 5;
var anyRandomBlah: String = cookies.data.anyRandomVariable;
// now it's loaded
(following the comments above...)
Yes, pretty much along those lines. I was asking questions to try to get you to see that you do actually have variables/data which you'd save. =b
If you want to save in the middle of the level, it is up to you... I don't know how you've designed your game, but basically you can write the saving code to trigger whenever you want based on any conditions you want.
For saving in the middle of levels, since you are handling the saving and loading you could try having a value like level "7.5" or whatever notation you want to indicate a level that is partway done. Or just use whole numbers like 750 and treat it as a percentage (level 7 at 50% done). And so forth. It's up to you.
A very similar question has been asked how to save a current frame value using a SharedObject. In your case, replace 'current frame' with whatever values you want to save:
Actionscript 3 saving currentframe location to local hard drive?
I asked similar question sometime ago, but I am making new one to be much more specific with my question with some example!
I´ve found this code snippet/tutorial from googling, but I cant seem to figure out how to modify it for my needs:
// open a local shared object called "myStuff", if there is no such object - create a new one
var savedstuff:SharedObject = SharedObject.getLocal("myStuff");
// manage buttons
btnSave.addEventListener(MouseEvent.CLICK, SaveData);
btnLoad.addEventListener(MouseEvent.CLICK, LoadData);
function SaveData(MouseEvent){
savedstuff.data.username = nameField.text // changes var username in sharedobject
savedstuff.flush(); // saves data on hard drive
}
function LoadData(MouseEvent){
if(savedstuff.size>0){ // checks if there is something saved
nameField.text = savedstuff.data.username} // change field text to username variable
}
// if something was saved before, show it on start
if(savedstuff.size>0){
nameField.text = savedstuff.data.username}
So what I am trying to figure out how to do, is to save users current frame to local hard drive, as my flash progress is based on frame location. yeah, so how do I modify it so that it stores data of current frame? and how about current frame inside movieclip, if that is makes things different?
help MUUUCH appreciated! thanks!
In your example it looks like it is already saving something to the shared object:
savedstuff.data.username = nameField.text;
Just replace it with the movie clip frame value instead (and probably under a different property name other then "username").
Then on load, there is another line where it loads the data:
nameField.text = savedstuff.data.username;
It would be the same way, except replace "username" with whatever property name you choose. Then you may have to parse into an int again and use it to restore progress however way you have it set up.