I'm trying to make a inventory using an array in a game I'm making.
What I need is a way to combine a number with a variable, something like this:
itemBoxNumber = "itemBox" + currentItemBox;
//In this case itemBoxNumber would say itemBox1
that I could use to replace the itemBox1.
function itemsMenuUpdate():void
{
for (var a:int = 0; a<maxInventory; a++){
var currentItemBox:Number = 1;
if(~inventory.indexOf("Potion")){
mainMenu.itemBox1.gotoAndStop("Potion");
}
if(~inventory.indexOf("Hi-Potion")){
mainMenu.itemBox1.gotoAndStop("Hi-Potion");
}
}
}
I can only find working methods for AS2. Any help is appreciated with this.
If I remember right, you can call a method from a String in AS3.
For example, if you want to call the method itemBox1 on mainMenu you can do:
mainMenu["itemBox1"]
Which is the same as:
mainMenu.itemBox1
So in your example you could do this:
mainMenu[itemBoxNumber].gotoAndStop("Potion");
You can retrieve the child display object via a name (which you need to set prior). So:
var itemBoxNumber = "itemBox" + currentItemBox;
mainMenu.getChildByName(itemBoxNumber).gotoAndStop("Potion"); //TODO ensure the child was given such a name
Make a LabelNumber Class, which has both a String label and a Number. You can then add a method that returns a String of the label and the number combined, while keeping the values independent of one another.
Related
i am wondering is there some syntax in Kotlin for declaring a variable which will be equal to a function which takes several parameters 1 of which is said variable?
class Player(var deck: MutableList<String> = Deck().deck) {
var playerHand = Deck().deal(deck, playerHand, 6)
}
class Deck {
var deck = mutableListOf<String>()
fun deal( from: MutableList<String>, to: MutableList<String>, num: Int ){
var temp = from.slice(0 .. num).toMutableList()
to.addAll(temp)
from.removeAll(temp)
}
}
So i basically want to transfer N amount of cards from a deck inside a Deck class to a variable playerHand in Player class. I know there are numerous other ways of doing it, i just wanted to know is there a way to create a var and assign a value to it using a function that takes that var as a parameter..
I stared at your code for a while and finally realized what you're asking, I think: You want playerHand to be a MutableList whose initial values come from calling Deck().deal(). Like #broot says in the comment, the standard way to do this is to use also. I'm also assuming it's some kind of typo that you are instantiating a new Deck instance to deal from, when you already have a deck property in your class. However, that property should probably be a Deck instance directly so you still have access to the Deck functionality that goes with it.
You might consider making your Deck class implement MutableList using a delegate to simplify how it is referenced and used. Now you can directly use existing MutableList functions like shuffle() directly on your Deck instance. And I would use LinkedList specifically as the delegate because it is more efficient than the default returned by mutableListOf at removing values from the top.
I would also make the deal function simply a dealTo function because it should be assumed a Deck is only going to deal from itself.
Putting that all together:
class Player(var deck: Deck = Deck()) {
val playerHand = mutableListOf<String>().also {
deck.dealTo(it, 6)
}
}
class Deck: MutableList<String> by LinkedList<String>() {
fun dealTo(to: MutableList<String>, num: Int) {
repeat(num) {
to += removeFirst()
}
}
}
I've spent nearly 1 week to learn working with objects instead of arrays. I had thought it was easy to call them and created some objects and set their properties. However I can't access them now, I tried this:
function onBoxClick(event:MouseEvent):void {
var str:String = event.currentTarget.name;
trace(str);
str = str.substring(str.indexOf("_") + 1);
trace(getChildByName("copy_" + str)); // trying to trace an object by name
}
My question is if there's a practical way of dealing with objects, otherwise what's the purpose of using them.
Edit: Here's my function that I use to create movieclips and other things:
function addBoxes(isUpdate:Boolean):void {
var copyOne:Object = getReadOnlyValues();
copyOne.name = "copy_" + num;
// Set default mc1 settings
var settings1:Object = copyOne.mc1Settings;
for(var num2:String in settings1) {
copyOne.mc1[num2] = settings1[num2];
}
// Set default mc1text settings
var settings2:Object = copyOne.mc1TextSettings;
for(var num3:String in settings2) {
copyOne.mc1Text[num3] = settings2[num3];
}
copyOne.mc1.x = nextXpos;
copyOne.mc1.name = "captionBox_" + num;
addChild(copyOne.mc1);
copyOne.mc1.addEventListener(MouseEvent.CLICK, onCaptionClick);
copyOne.mc1Text.name = "captionBoxText_" + num;
copyOne.mc1.addChild(copyOne.mc1Text);
// ---------------------------------------------------------------
// Set default mc2 settings
var settings4:Object = copyOne.mc2Settings;
for(var num4:String in settings4) {
copyOne.mc2[num4] = settings4[num4];
}
// Set default mc2text settings
var settings5:Object = copyOne.mc2TextSettings;
for(var num5:String in settings5) {
copyOne.mc2Text[num5] = settings5[num5];
}
copyOne.mc2.x = nextXpos;
copyOne.mc2.y = copyOne.mc1.height;
copyOne.mc2.name = "box2_" + num;
addChild(copyOne.mc2);
copyOne.mc2Text.name = "box2BoxText_" + num;
copyOne.mc2.addChild(copyOne.mc2Text);
copyOne.mc2.addEventListener(MouseEvent.CLICK, onBoxClick);
if (num / subunits is int) {
trace (num);
// createMc("normalBox", true);
}
nextXpos = nextXpos + copyOne.mc2.width;
// traceObj(copyOne);
// traceObj(getReadOnlyValues());
}
I called this function in a loop so I created many movieclips. Now I can't access objects' properties and their childen (e.g textfield).
Objects I have on stage: Movieclips and textfields
Where they come from: The function above
What I'm trying to do with them: Tracing movieclips and textfields (that are holded by objects) to change their children (textfield) text
What happens instead of what I expect: Trace code outputs undefined instead of giving me object type trace(getChildByName("copy_" + str)); // trying to trace an object by name
Is there a practical way of accessing an object whose name is "copy_1" and its property whose name is "box2_1" (movieclip)?
One problem I see is the "copyOne" object has been created within the scope of "addBoxes", so it will no longer exist outside of this function.
Another is you're trying to access an Object via getChildByName, which only addresses displayObjects of the displayObjectContainer you are calling from.
If you want to loosely keep track of variables with things like Objects or MovieClips (which are both dynamic-style objects that let you add properties to them as you wish), just use MovieClips to house your values. The movieClips, being on the stage, will be retained in memory until removed from the displayList (stage).
Also, check out the Dictionary, a sort of key/value based way of storing collections of objects.
Better yet, if you use strongly-typed custom objects (creating your own classes to extend MCs, and adding your own public or private methods and values), there are benefits such as using Vectors (fancy, fast arrays that are compatible with any Object type you choose).
I don't really know if I understood your question or not, but as #ozmachine said in his answer, you can not use getChildByName, instead I think that you can take a look on this, may be it can help :
var container:DisplayObjectContainer = this;
function getReadOnlyValues():Object {
return {
mc1: new box(),
mc1: {
name: 'mc1_',
alpha: 1,
x: 0,
y: 0,
width: 30,
height: 25
},
mc1Text: new TextField(),
mc1Text: {
text: 'test',
x: 0,
y: 0,
selectable: false,
multiline: false,
wordWrap: false
}
}
};
// create 5 objects
for(var i=0; i<5; i++){
container['copy_'+i] = getReadOnlyValues();
var obj:Object = getObjectByName('copy_'+i);
obj.mc1.alpha = 1;
obj.mc1.x = 0;
obj.mc1.y = 50 * i;
obj.mc1.width = 100;
obj.mc1.addChild(obj.mc1Text);
obj.mc1Text.text = 'test_' + i;
addChild(obj.mc1);
}
// get object by name
function getObjectByName(name:String):Object {
return container[name];
}
// change the text of the 4th button
stage.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
var obj:Object = getObjectByName('copy_3');
obj.mc1Text.text = 'new text';
})
Array and Object are both data structures.
Data means some form of information.
Data structure means some form of information being stored in a certain way.
Array and Object are two different ways to store information.
Arrays identify data with integer numbers.
An integer number to identify a single element of an array is called an index
Arrays are ideal to represent a list of similar things that belong to each other.
var names:Array = ["John", "Paul", "George", "Ringo"];
This often means that the elements of an array are of the same type, like in the example above.
But they don't have to:
var numbers:Array = [42, "twenty-five", "XIIV"];
For the above examples it's easy to answer the questions "What are the names of the four beatles?", "What different representations of numbers did you stumble upon during your trip through the historic town?". Other questions are harder or impossible to answer. "What Roman numerals did you stumble upon in the historic town?"
Objects identify data with names.
A name to identify a single element of an object is called a property
Objects are ideal to represent a list of dissimilar things that belong to each other.
var paula:Object = {age:37, name:"Paula", hairColor:0x123456};
This often means that the elements of an object are of different type, like in the example above.
But they don't have to:
var car:Object = {manufacturer:"Porsche", color:"red", sound:"wroooooom", soundOfDriver:"weeeeeeeeeeee"};
Considering this, let's take a look at your code and see how it applies.
The big picture is that you have a function addBoxes that you call multiple times. As one function should have one purpose, this function will do something similar every time it is executed. Uh-Oh: "similar". Whatever the result of this function is, it should go into an array. Each call to that function would be an element of the array. You can see this clearly on your use of "num" to identify whatever is happening in your current run of the function.
What data is present in your function?
copyOne
mc1
mc1Text
mc2
mc2Text
copyOne is a troublemaker here and what causes your confusion. It's trying to do everything at once and therefore you are not able to think clearly about when to use a Array and when Object. One would call it a god object. And that's not a good object to have around.
Your choice for variable names is very bad.
You choose super generic names like "mcX" only to later add a name property to it that describes what it truly is.
But even that doesn't hold true for whatever "Box2" is supposed to be.
Choose names so that it'S easy to understand what something in your code is.
It looks like you created all or parts of this structure jsut for this question and therefore lacked meaningful names.
I highly recommend that you do not learn by such made up projects. But from the real world.
I will therefore impose the following goal:
mc1 and mc1Text represent a caption
mc2 and mc2Text represent a content
With all this, I ask again:
What data is present in your function?
captionBox
captionText
contentBox
contentText
Both caption and content consist of a box and a text.
These are different things, so caption and content are each an object with properties "box" and "text"
One could think that due to this similarity, they both should go into an array.
But I beg to differ. A caption and a text are not the same thing. You deal with captions and texts differently. Walking on the streets you might catch a big caption in the news quickly, but not a lengthy text. That's why each of them should be a property of the object that's created in the function.
Here's somewhat of a conclusion:
var allBoxes:Array = []; // array to store the similar results of every function call
function createBoxes():void
{
var boxes:Object = {};
//the box consists of caption & content, both bying of the same type, but are containing different data
boxes.caption = {box:{}, text:{}}; //caption
boxes.content = {box:{}, text:{}}; //content
allBoxes.push(boxes);
}
This is it. That's how and why I would model your data with objects and arrays.
But it doesn't end here. My conclusion lacks a lot of the code you posted.
While the above is mostly language independent, the missing code is specific to Actionscript and not just on how to model data. It's as follows...
As3 is object oriented.
This is good, because the above conclusion has a lot of objects in it.
To define how some object is/does/moves/farts/etc, one creates classes.
The following changes take place (for reasons out of the scope of this answer):
createBoxes (formerly known as addBoxes) calls the constructor of
a class "CaptionAndContent" that extends Sprite.
There's no more need to explicitely create an object "boxes" as the constructor does exactly that.
The caption and content will not have a property "box", because
they can be the box themselves. This is exactly how it's done in the
code of the question. The default settings are set in the constructors of their classes.
Here's reduced snippet of code that hopefully illustrates how the classes could look like.
Each class should be in its own file, with the necessary imports, package block and the additional functionality that you did not specify in your question.
public class CaptionAndContent extends Sprite
{
private var caption:Caption;
private var content:Content;
public function CaptionAndContent(captionText:String = "", contentText:String = "")
{
caption = new Caption(captionText);
addChild(caption);
content = new Content(contentText);
content.y = caption.height;
addChild(content);
}
}
public class ClickableBoxWithText extends Sprite
{
protected var textField:TextField;
public function ClickableBoxWithText(text:String = "")
{
textField = new TextField();
textField.text = text;
addChild(textField);
addEventListener(MouseEvent.CLICK, onClick);
}
protected function onClick(mouseEvent:MouseEvent):void
{
//override this in a sublclass
}
}
public class Caption extends ClickableBoxWithText
{
public function Caption(text:String = "")
{
super(text);
// apply all the default settings of caption here.
}
}
public class Content extends ClickableBoxWithText
{
public function Content(text:String = "")
{
super(text);
// apply all the default settings of content here.
}
}
Using them would look something like this:
var allBoxes:Array = []; // array to store the similar results of every function call
function createBoxes():void
{
var captionAndContent:CaptionAndContent = new CaptionAndContent("This is the caption...", "...for this content");
captionAndContent.x = nextXpos;
addChild(captionAndContent);
allBoxes.push(captionAndContent);
}
Last but not least, the identification problem in the click handler.
Your question already contains the answer:
event.currentTarget
That's the reference to the object that was clicked on.
In my code it would be
mouseEvent.currentTarget
This identifies the object already. It's pointless to look up one of its properties (its name for example) in order to search all the objects for that name, just to identify the same object that you already had to identify (without a name) in order to get the name.
You aren't identifying the objects by name anyway. What differs between the names and what supposedly makes them unique is a number at their end. As pointed out in this answer, this is what's called an index and the thing you are trying to identify with it should go into an array. In my example codes, this is allBoxes.
i have a class (TheList.as). in which i have an array "Data" and it has a couple of values. Then i have a loop through which i am creating a scrollable list which uses the values from "Data" array. [I am trying make a unit converter]
Then i have another class "Units.as". In that class i have created three instances of "TheList". A main list ("myList"), and to sublists "ListFrom" and "ListTo". They are using values from "Data" array. Now i have text field whose value changes to whatever item is clicked. When i click "Angle" in the main list, i want the sublists to get populated with ("Degree", "Radian" etc)..
Here is what i tried
if(myList._TextLabel.text == "Angle")
{
ListFrom.Data = ["Degree", "Radian"];
}
But nothing happens, i do not get any error either. When i do this in an "ENTER_FRAME" event and trace (ListFrom.Data), i can see that the values change, but they do not get assigned to the list items in the list. I would really appreciate the help. Thanks!
Here are complete Classes for understanding the situation better(the code is pretty messy, as i am a newbie to OOP)
TheList.as: http://pastebin.com/FLy5QV9i
Units.as : http://pastebin.com/z2CcHZzC
where you call ListFrom.Data = ["Degree","Radian"], make sure when the data changed, the renders in the ListFrom have been set new data. for example, you may use MyRender in ListFrom for show, you should debug in the set data method in MyRender.
you should call the code below after you call ListFrom.Data = ["Degree","Radian"];
for (var i:int = 0; i < Data.legnth;i++) {
var render:MyRender = ListFrom[i] as MyRender;
if (render) {
render.data = Data[i];
} else {
var render:MyRender = new MyRender();
render.data = Data[i];
ListFrom.addChild(render);
}
}
You can use event listeners, singleton classes or reference one class to another, depending on the style you want. All are equally valid and fast / efficient.
I made an invetory in AS3 which allows me to put items on slots in a closet, or in slots in the inventory. It completely works, but there is one problem.
In the game you are supposed to be able to buy new items and add them to the closet. I want this to be saved so that it is available the next time you play.
To do this, I want to save an Array to a SharedObject, then create the items dynamically from the array.
Right now I'm using the old fashioned hard coding for each object;
Itemwrench = new WrenchItem();
Itemwrench.x = par.toolCloset.kast_1.slotTC1.x + 400;
Itemwrench.y = par.toolCloset.kast_1.slotTC1.y + 245;
Itemwrench.gotoAndStop(2);
Itemwrench.name = "slotTC1";
Itemwrench.TC = 1;
NotinventoryParentTC.addChild(Itemwrench);
However, to add them dynamically I'd have to use getChildByName before it is added to the stage, which is not possible.
If possible could you show me how to do this correctly?
The information you need:
-The items are all stored in a closet with slots(Instances in the closet movieclip).
-The items need to get the name slotTC + the integer in a for loop.
-The name of the items change according to the slot number they are assigned when you take them out of the closet or put them back into the closet.
for(var i:int = 0; i < itemsInTC.length - itemsInTC.indexOf(e.currentTarget.name) - 1; i++)
{
nextSlotTC = "slotTC" + (itemsInTC.indexOf(e.currentTarget.name) +2 +i);
trace("Next Slot: " + nextSlotTC);
TempStrTC = "slotTC" + (itemsInTC.indexOf(e.currentTarget.name) +1 +i).toString();
trace("temp string: " + TempStrTC);
NotinventoryParentTC.getChildByName(nextSlotTC).x =
par.toolCloset.kast_1.getChildByName(TempStrTC).x + 400;
NotinventoryParentTC.getChildByName(nextSlotTC).y =
par.toolCloset.kast_1.getChildByName(TempStrTC).y + 245;
if(Boolean(NotinventoryParentTC.getChildByName(nextSlotTC)))
{
NotinventoryParentTC.getChildByName(nextSlotTC).name = TempStrTC;
}
}
This way I assign a new name and place them in the slot with the new name they received.
So now my question:
How do make it so that you can save the items to a shared object so that they are in the closet the next time you play the game.
Sorry for the long question.
Thanks in advance,
Milan.
You cannot directly store a DisplayObject in a SharedObject, as it contains memory links which will not be valid if you load such an object. A comon way to work around this is to store a significant data portion of that object. For example, you devise a following structure:
class SlotStructure {
public var slotID:int;
public var itemID:int;
public var itemName:String;
public var itemParameters:Array; // stuff simple types here
}
Then, for each of your items in inventory, you generate a SlotStructure object describing a particular inventory object. For your wrench it could look like this:
var ss:SlotStructure=new SlotStructure();
ss.slotID=1;
ss.itemID=getID(item); // assuming a function that returns a type of an item
ss.itemName=item.name;
ss.itemParameters=new Array();
for (var param:String in item) ss.itemParameters.push({name:param,value:item[param]});
Then you store an array of these into your SharedObject. To retrieve an inventory from a SharedObject you do:
public static const
registerClassAlias("SlotStructure",SlotStructure); // to be able to typecast from SO
for (var i:int=0;i<slots.length;i++) {
var ss:SlotStructure=slots[i];
var item:Item=new getClassFromID(ss.itemID)(); // a function that returns class
// say 1 - wrench, 2 - short sword, 3 - long sword, etc, one type=one ID
for each (var o:Object in ss.itemParameters)
item[o.name]=o.value;
placeIntoSlot(item,ss.slotID); // this does manipulation with x&y and display
}
A function getClassByID() might look like this:
private static const CLASSES:Array=
[StoneItem,WrenchItem,ShortswordItem,LongswordItem,...];
// manually stuff all your items in this!
public function getClassByID(id:int):Class {
return CLASSES[id];
}
The entire solution can be tailored to particular task, for example, in my game I have gems, that differ by location, type, size and score, so I store just these values and then I create new gems, set location, type, size and score with one function that sets all the other relevant parameters of that gem to align with stored info, and call it after making a gem with new Gem(). Your items might too be only worthy of a class name and ID in the class table, so store these with slot numbers and create objects that will have all their properties already set.
I guess a step back is in order. My original question is at the bottom of this post for reference.
I am writing a word guessing game and wanted a way to:
1. Given a word length of 2 - 10 characters, randomly generate a valid english word to guess
2.given a 2 - 10 character guess, ensure that it is a valid english word.
I created a vector of 9 objects, one for each word length and dynamically created 172000
property/ value pairs using the words from a word list to name the properties and setting their value to true. The inner loop is:
for (i = 0; i < _WordCount[wordLength] - 2; i)
{
_WordsList[wordLength]["" + _WordsVector[wordLength][i++]] = true;
}
To validate a word , the following lookup returns true if valid:
function Validate(key:String):Boolean
{
return _WordsList[key.length - 2][key]
}
I transferred them from a vector to objects to take advantage of the hash take lookup of the properties. Haven't looked at how much memory this all takes but it's been a useful learning exercise.
I just wasn't sure how best to randomly choose a property from one of the objects. I was thinking of validating whatever method I chose by generating 1000 000 words and analyzing the statistics of the distribution.
So I suppose my question should really first be am I better off using some other approach such as keeping the lists in vectors and doing a search each time ?
Original question
Newbie first question:
I read a thread that said that traversal order in a for.. in is determined by a hash table and appears random.
I'm looking for a good way to randomly select a property in an object. Would the first element in a for .. in traversing the properties, or perhaps the random nth element in the iteration be truly random. I'd like to ensure that there is approximately an equal probability of accessing a given property. The Objects have between approximately 100 and 20000 properties. Other approaches ?
thanks.
Looking at the scenario you described in your edited question, I'd suggest using a Vector.<String> and your map object.
You can store all your keys in the vector and map them in the object, then you can select a random numeric key in the vector and use the result as a key in the map object.
To make it clear, take a look at this simple example:
var keys:Vector.<String> = new Vector.<String>();
var map:Object = { };
function add(key:String, value:*):void
{
keys.push(key);
map[key] = value;
}
function getRandom():*
{
var randomKey = keys[int(Math.random() * keys.length)];
return map[randomKey];
}
And you can use it like this:
add("a", "x");
add("b", "y");
add("c", "z");
var radomValue:* = getRandom();
Using Object instead of String
Instead of storing the strings you can store objects that have the string inside of them,
something like:
public class Word
{
public var value:String;
public var length:int;
public function Word(value:String)
{
this.value = value;
this.length = value.length;
}
}
Use this object as value instead of the string, but you need to change your map object to be a Dictionary:
var map:Dictionary = new Dictionary();
function add(key:Word, value:*):void
{
keys.push(key);
map[key] = value;
}
This way you won't duplicate every word (but will have a little class overhead).