I'll explain a little before asking my question ... I've created numerous games which load and unload off a main menu.
The player enters their name on the main menu before playing any games and when the player completes a game I want to save their time (taken to complete the game) and unload this time back into the main menu.
Is there any way of saving the times using AS3 to a word document or something like this? I can't send the times to my website with php because the games will be used within a competition and it all needs to work with the internet.
Any ideas guys?
Edit:
var dataloader:URLLoader = new URLLoader();
var dataarray:Array = new Array(); // do this where you intialise other vars
function preparesave()
{
dataloader.load(new URLRequest("savedata.txt"));
}
dataloader.addEventListener(Event.COMPLETE,savedata);
function savedata (event:Event)
{
dataarray = event.target.data.split(/\n/);
dataarray.push(MyTimer);
var bytes:ByteArray = new ByteArray();
var fileRef:FileReference = new FileReference();
for (var i = 0; i < dataarray.length;i++)
{
bytes.writeMultiByte(dataarray[i] + "\n", "iso-8859-1");
bytes.writeMultiByte('English Game Time: ',"iso-8859-1");
bytes.writeMultiByte(HourText.text.toString(),"iso-8859-1");
bytes.writeMultiByte(':',"iso-8859-1");
bytes.writeMultiByte(MinuteText.text.toString(),"iso-8859-1");
bytes.writeMultiByte(':',"iso-8859-1");
bytes.writeMultiByte(SecondText.text.toString(),"iso-8859-1");
}
fileRef.save(bytes,"savedata.txt")
}
You could use a cookie(sharedObject) for this purpuse. See http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/SharedObject.html
var data:SharedObject = SharedObject.getLocal("userdata");
data.startTime = new Date().time;
If you need the startTime again you could just retrieve it by the cookie and variable name.
For small amounts of data, you're best-off just going with automaticoo's solution by using SharedObjects.. they take minimal time to set up and are an efficient way of storing small amounts of data.
However, if it's not for you, you could always load/save to a text file. You can do this using ByteArrays and FileReferences.
Since you brought up saving to a word file, I would suggest saving to a text file would be the best way of achieving your goal (although other than your word doc. suggestion, i'm not sure.. your aim is sort of unclear)
Here is a seriously quick demonstration of saving to a text file.. If you need help loading it too, let me know.
function savedata() {
var bytes:ByteArray = new ByteArray();
var fileRef:FileReference = new FileReference();
bytes.writeMultiByte(playername + "\n", "iso-8859-1");
bytes.writeMultiByte(playerscore.toString(),"iso-8859-1");
fileRef.save(bytes,"savedata.txt");
}
This is pretty simply.. By using writeMultiByte, we can write as much data as needed before saving our text file! This is especially useful if you have arrays of data you need to save.
Anyway, the "iso-8859-1" is simply a reference to the character set / file-format being used.. I'm not sure if you can simply write utf-16 etc. instead.. I've always just used it as it is written above.
The result in the text file here will be the following:
Peter
9547 (but up one line)
To load, you can just split data by line, resulting in an array full of strings and ints/numbers (however your scores may work). Again, let me know if you need help doing that.
I'm not sure if this is the most efficient method of achieving what you're after since your goal isn't entirely clear to me, but it has been a very useful way for myself in storing large amounts of data, which I'm guessing you are wanting to do if you are compounding all the scores of players.
edit:
Okay, to the questions you posed below:
To save more than one user's score is simple. Simply have a single array which loads the data from the text file (if the text file exists), then adds the new score to the end of the array, then saves back to the text file.
Here is an example:
var dataloader:URLLoader = new URLLoader();
var dataarray:Array = new Array(); // do this where you intialise other vars
function preparesave() {
dataloader.load(new URLRequest("savedata.txt"));
}
dataloader.addEventListener(Event.COMPLETE,savedata);
function savedata (event:Event) {
dataarray = event.target.data.split(/\n/);
dataarray.push(playername);
dataarray.push(playerscore);
var bytes:ByteArray = new ByteArray();
var fileRef:FileReference = new FileReference();
for (var i = 0; i < dataarray.length;i++) {
bytes.writeMultiByte(dataarray[i] + "\n", "iso-8859-1");
}
fileRef.save(bytes,"savedata.txt")
}
What this does is take any existing save data, pushes it to an array (where one line on your save file is one array data entry), adds your appropriate data, and pushes it back out to the save file.
(2) To load this in whilst in the main menu, simply place this code in the frame of the main menu..
dataloader.load(new URLRequest("savedata.txt"));
dataloader.addEventListener(Event.COMPLETE,loaddata);
function loaddata (event:Event) {
dataarray = event.target.data.split(/\n/);
}
This will load all your existing data into an array. What you do from then on is up to you.
Related
Currently I have an intro screen to my flash file which has two objects.
A button which will load an external flash file using:
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("flashgame.swf");
The second thing is a Numeric Stepper, which will be from 1 to 10. If the user selects a number e.g. 3 then the game speed I have set in the flashgame.swf should be changed
Such as:
var gameSpeed:uint = 10 * numericStepper.value;
But I think my problem is coming into place because the stepper and gamespeed are from different files.
Anyone got any idea please?
I have also tried creating a stepper in the game file and used this code:
var gameLevel:NumericStepper = new NumericStepper();
gameLevel.maximum = 10;
gameLevel.minimum = 1;
addChild(gameLevel);
var gameSpeed:uint = 10 * gameLevel.value;
For some reason the stepper just flashes on the stage, no errors come up and the game doesn't work
When you execute you code, the stepper has no chance to wait for user input.
There is no time between theese two instructions.
addChild(gameLevel);
var gameSpeed:uint = 10 * gameLevel.value;
You should wait for user input in your NumericStepper, and then, on user event, set the game speed.
Edit: Yeah I know it's kinda sad to type out all this code (especially since some people wouldn't even be grateful enough to say thanks) but I think this question is important enough to justify the code as it may be helpful to others in future also.
Hi,
You were close. In your game file you could have put a var _setgameSpeed and then from Intro you could adjust it by flashgame._setgameSpeed = gameSpeed; It's a bit more complicated though since you also have to setup a reference to flashgame in the first place. Let me explain...
Ideally you want to put all your code in one place (an .as file would be best but...) if you would rather use timeline then you should create a new empty layer called "actions" and put all your code in the first frame of that.
Also change your button to a movieClip type and remove any code within it since everything will be controlled by the code in "actions" layer. In the example I have that movieclip on the stage with instance name of "btn_load_SWF"
Intro.swf (Parent SWF file)
var my_Game_Swf:MovieClip; //reference name when accessing "flashgame.swf"
var _SWF_is_loaded:Boolean = false; //initial value
var set_gameSpeed:int; //temp value holder for speed
var swf_loader:Loader = new Loader();
btn_load_SWF.buttonMode = true; //instance name of movieclip used as "load" button
btn_load_SWF.addEventListener(MouseEvent.CLICK, load_Game_SWF);
function load_Game_SWF (event:MouseEvent) : void
{
//set_gameSpeed = 10 * numericStepper.value;
set_gameSpeed = 100; //manual set cos I dont have the above numericStepper
if ( _SWF_is_loaded == true)
{
stage.removeChild(swf_loader);
swf_loader.load ( new URLRequest ("flashgame.swf") );
}
else
{ swf_loader.load ( new URLRequest ("flashgame.swf") ); }
swf_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, Game_SWF_ready);
}
function Game_SWF_ready (evt:Event) : void
{
swf_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, Game_SWF_ready);
//Treat the Loader contents (flashgame.swf) as a MovieClip named "my_Game_Swf"
my_Game_Swf = swf_loader.content as MovieClip;
my_Game_Swf.gameSpeed = set_gameSpeed; //update gameSpeed variable in flashgame.swf
//also adjust SWF placement (.x and .y positions etc) here if necessary
stage.addChild(my_Game_Swf);
_SWF_is_loaded = true;
}
Now in you flashgame file make sure the there's also an actions layers and put in code like this below then compile it first before debugging the Intro/Parent file. When your Intro loads the flashgame.swf it should load an swf that already has the code below compiled.
flashgame.swf
var gameSpeed:int;
gameSpeed = 0; //initial value & will be changed by parent
addEventListener(Event.ADDED_TO_STAGE, onAdded_toStage);
function onAdded_toStage (e:Event):void
{
trace("trace gameSpeed is.." + String(gameSpeed)); //confirm speed in Debugger
//*** Example usage ***
var my_shape:Shape = new Shape();
my_shape.graphics.lineStyle(5, 0xFF0000, 5);
my_shape.graphics.moveTo(10, 50);
my_shape.graphics.lineTo(gameSpeed * 10, 50); //this line length is affected by gameSpeed as set in parent SWF
addChild(my_shape);
}
The key line in intro.swf is this: my_Game_Swf.gameSpeed = set_gameSpeed; as it updates a variable in flashgame.swf (referred as my_Game_Swf) with an amount that is taken from a variable in the Parent SWF.
This is just one way you can access information between two separate SWF files. Hope it helps out.
I have a class assignment where I have to create a photo gallery and one of the stipulations is that the images need to be loaded from an external source. The reference code from our assignment was:
var myRequest:URLRequest = new URLRequest("bw1.jpg");
myLoader.load(myRequest);
addChild(myLoader);
My specific needs requires me to have up to 2 copies of the images on the screen at once, one as a thumbnail and a second as a fullsized image. I want it so that when a user clicks a thumbnail a new instance of the picture is created scaled to fullsize with 0 alpha and the current selected picture decreases in alpha as the new pic increases in alpha. Once the old pic is no longer visible it is removed from the stage.
I am having trouble figuring out how to create a copy of the image in actionscript and also my for loop seems to not be executing properly as it only ever goes through one iteration even when I have no error messages.
Here is a link to my code via pastebin: http://pastebin.com/iadgKgsk but to save you from having to switch back and forth between I will also past it here
import flash.display.Bitmap;
//creates a loader for each picture to be loaded, I know I could have an empty array
that I add to but created a full array to test.
var loaders:Array = [new Loader(),new Loader(), new Loader(), new Loader(), new
Loader(), new Loader(), new Loader(), new Loader(), new Loader(), new Loader()];
//stores the url request for each image to be loaded
var Requests:Array =[new URLRequest("pic1.jpg"),new URLRequest("pic2.jpg"),new
URLRequest("pic3.jpg"),
new URLRequest("pic4.jpg"),new URLRequest("pic5.jpg"),new URLRequest("pic6.jpg"), new
URLRequest("pic7.jpg"),
new URLRequest("pic8.jpg"), new URLRequest("pic9.jpg"),new URLRequest("pic10.jpg")];
//creates 2 empty arrays one to store the thumbnail sized pics the other fullsized.
Ideally I want one Array holding the bitmap data and the other holding the thumbnail
instances since I only need 2
// fullsized images at a time I can just create a new one and erase the old one in a
single function.
var pics:Array = new Array();
var pics2:Array = new Array();
//defines an empty bitMap variable as a placeholder for which to copy from redefined in
every iteration of the loop
var test:Bitmap;
//loops through every loader
for (var i in loaders);
{
// loads the loader and url request and creates a picture
loaders[i].load(Requests[i]);
//waits for the pic to load
loaders[i].contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
//after loader is loaded create pic from the data
function loadComplete(evt:Event):void
{
var i = "Test";
trace(i);
test= evt.target.content as Bitmap;
pics2[i] = test;
pics[i] =new Bitmap(test.bitmapData);
pics2[i] =new Bitmap(test.bitmapData);
//creates an image on the stage at runtime to help debug
var pic1 = new Bitmap(test.bitmapData);
addChild(pics[i])
pic1.scaleX = 0.138427734375;
pic1.scaleY = 0.1384114583333333;
pic1.x = 204;
pic1.y = 20.6;
pic1.alpha = .25;
var pic2:Bitmap = new Bitmap(test.bitmapData);
pic2.x =100;
pic2.y =100;
pic2.scaleX =.33;
pic2.scaleY=.33;
addChild(pic2);
loaders[i].contentLoaderInfo.removeEventListener(Event.COMPLETE,
loadComplete)
}
}
Your for loop is setup in a way that it would attempt to iterate for each property of the loaders instance which isn't what you want. You may want to investigate other AS3 bulk loader options too there are already some scripts out there to help take care of this.
Your loop should look like
for(var i=0; i<loaders.length; i++)
{
var curLoader = loaders[i].load(Requests[i]);
You can also see an example I put together that loads a bunch of images flipbook style animation (images generated with blender):
http://www.shaunhusain.com/DrawTextRandomly/
http://www.shaunhusain.com/DrawTextRandomly/srcview/
look in the utils.imageLoader package, I made two classes there that deal with loading the images, since they were output from Blender with sequential file names I just used some loops to load the images based on a numeric counter, in your case you could switch it to use a source array of images to load.
In the BatchImageLoader class I have a variable for how many loaders to allow it to make, I wanted to see the difference in performance in terms of memory vs load time (will depend on runtime environment too but I wanted to get a general idea, ended with leaving it at using 90 loaders at most).
I have spent many hours searching the internet and have come up empty handed. My problem is that i am trying to create a flash file that loops every 10 seconds and changes 2 dynamic text fields each time it loops.
var xmlData:XML = new XML();
var theURL_ur:URLRequest = new URLRequest("shout.xml");
var loader_ul:URLLoader = new URLLoader(theURL_ur);
function fileLoaded(e:Event):void
{
xmlData = XML(loader_ul.data);
show_txt.text = xmlData.SERVERTITLE;
song_txt.text = xmlData.SONGTITLE;
}
The dynamic fields are set correctly and are all in the same scene but as i said, it does not load the right fields and when it does work it caches which i do not want it to do.
Maybe set a "counter" variable that increments and append your URL with shout.xml?c=[counter]
I'm making a simple tool that will let the user eventually save a image to the disk.
To make this, I'm using the FileStream class and its writeBytes() method.
This is working nicelly. The problem occurrs when I tried to show the save progress with a simple mx:ProgressBar. I've tried some approaches, but none seems to work.
Here is the ActionScript piece of code:
private function save(file:File, image:MyImageClass):void {
var data:BitmapData = new BitmapData(width, height, true, 0x000000);
image.draw(data, _cols);
var bytes:ByteArray = new PNGEncoder().encode(data);
fileStream = new FileStream();
fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, onProgress);
fileStream.addEventListener(Event.CLOSE, onClose);
try {
fileStream.openAsync(file, FileMode.WRITE);
fileStream.writeBytes(bytes);
} catch (e:Error) {
Alert.show("Error trying to save the image: " + e.message);
} finally {
fileStream.close();
}
}
private function onProgress(event:OutputProgressEvent):void {
var progressRatio:Number = (1 - (event.bytesPending / event.bytesTotal));
progressBar.setProgress(progressRatio, 1);
trace(progressRatio);
if (event.bytesPending == 0)
event.target.close();
}
private function onClose(event:Event):void {
trace("closed");
}
And the mxml for the progress bar:
<mx:ProgressBar id="progressBar" mode="manual"/>
Executing this I got a frozen interface that is released when the file is totally saved and on the console I get all the traces at the same time. The progress bar stays on 0% until the interface is unfrozed and it goes to 100%.
I know that Flash is single thread, but I thought that the FileStream.openAsync() method should do the dirty work to make my interface responsible. It shouldn't be that hard to do this simple and (I think) common task.
The question is: what I'm doing wrong?
Thanks in advance
How big is the file being saved?
You code seems fine, in terms of saving the file. However, what I suspect is actually taking a long time is encoding the file to PNG format. Unfortunately, the PNGEncoder doesn't dispatch any progress events. But you should check out this project from Lee Burrows, or consider using Actionscript Workers to do the encoding in a different thread.
Quick way to prove it: add trace statements before/after you encode to PNG. Does the big delay correspond to this phase, or to the actual save?
If that doesn't help, please specify how big your file is, and whether you getting 0, 1, or multiple OUTPUT_PROGRESS events.
I agree with Sunil, but I'd like to add a thing or two.
First of all I'd suggest using the new method of the BitmapData class for encoding an image, because it's faster and easier. So your code would change to something like this:
var data:BitmapData = new BitmapData(width, height, true, 0x000000);
image.draw(data, _cols);
var bytes:ByteArray = data.encode(data.rect, new PNGEncoderOptions());
You can track the progress of the writing the file (although I suspect this isn't taking much time, like Sunil said) like this:
bytes.position = 0;
while(bytes.bytesAvailable > 0) {
fileStream.writeByte(bytes.readByte());
trace("progress:", bytes.position / bytes.length * 100); // multiply by 100 for percentage
}
Please note that you'll need a worker for this approach, else the progress will only update visually when the save is done.
Sunil is correct. Writing the file hardly takes a moment. It's decoding the byte array that's blocking the application. I implemented the following code to test it out.
private function save(file:File):void
{
var bytes:ByteArray = new ByteArray();
//233348 - very fast (requires 6000 x 10000 bitmap pixels)
//252790 - very fast (requires 6500 x 10000 bitmap pixels)
//2488990 - need a whole magnitude more of data in order to show the progress messages
for (var i:int = 0; i < 2488990; i++)
{
bytes.writeByte(1);
}
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, onProgress);
fileStream.addEventListener(Event.CLOSE, onClose);
try {
fileStream.openAsync(file, FileMode.WRITE);
fileStream.writeBytes(bytes);
} catch (e:Error) {
//Alert.show("Error trying to save the image: " + e.message);
} finally {
fileStream.close();
}
}
You're going to need a progress indicator on the decoding task rather than on the file writing task. Workers seem like the best solution to this, but it depends upon which version of the runtime you need to target.
My goal was to have an external text file config for a client. I didnt want to go through a crazy xml thing, I just wanted it to be simple to change.
I started with a urlLoader, and was able to dynamically generate an object no problem. This is the function which parses and sets the properties of the object.
function onLoaded(e:Event):void//initializes the config
{
var myString = String(e.target.data);
//trace(e.target.data);
//trace(myString);
var propsArray:Array = myString.split("\n");
for (var i = 0; i < propsArray.length; i++){
var thisLine:Array = propsArray[i].split("=");
var thisPropName:String = thisLine[0];
thisPropName = thisPropName.replace(rex,'');
var thisPropValue:String = thisLine[1];
thisPropValue = thisPropValue.replace(rex,'');
trace("thePropName is: " + thisPropName);
trace("thePropValue is: " + thisPropValue);
config[thisPropName] = thisPropValue;
}
}
The text file would just look something like:
gateway = "http://thePathto/theFile.php
toast = sonofabitch
timer = 5000
xSpeed = 5.0
That way, I could just put a little bit of as3 code in, type what things I wanted configured, then all I would have to do was type config.timer and
var myTimer:Timer = new Timer(Number(config.timer));
I think the problem is load order and scope. The config.timer is not created yet, so the timer is unable to access the value of the config.timer.
I'd look at using XML in future projects of this nature, however to answer your question:
I think the problem is load order and scope. The config.timer is not created yet, so the timer is unable to access the value of the config.timer.
Correct, you will need to initialize your Timer within the onLoaded() method, as the data will be received asynchronously and is not available until this happens.
ok not long ago i had created a download manager that uses this exact concept.
The link below will take you straight to the website where you can download the full swf including my source files. also this website is a good place for resources
http://ffiles.com/flash/web_applications_and_data/dynamic_download_manager_3529.html
Below is my loader:
addEventListener(Event.ENTER_FRAME, update);
var myLoader:URLLoader = new URLLoader();
myLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
myLoader.load(new URLRequest("settings.txt"));
myLoader.addEventListener(Event.COMPLETE, onDataLoad);
function onDataLoad(evt:Event)
{
box1.text = evt.target.data.Id_1;
box2.text = evt.target.data.Id_2;
box3.text = evt.target.data.Id_3;
box4.text = evt.target.data.Id_4;
box5.text = evt.target.data.Id_5;
}
Add some dynamic text boxes to stage and name them "box1, box2 ect..."
Now creat your text file:
Id_1=this is what ever you want
&Id_2=this is what ever you want
&Id_3=this is what ever you want
&Id_4=this is what ever you want
&Id_5=this is what ever you want
Hope this helps.