LibGdx object array - arrays

I have created an array of objects in my libGdx project like this:
Array<Bird> birds = new Array<Bird>();
Here Bird is one object class.
I want to draw 10 Bird objects one by one on the screen.So I did like this.
private void MakeBird() {
Bird b = objectFactory.createBird();
for (int i = 0; i < 10; i++) {
birds.add(b);
}
}
createBird method of objectFactory:
public Bird createBird() {
Bird bird = new Bird();
bird.setPosition(0,0);
return bird;
}
Here I want only 10 objects.But when I try to print the array size,it is incrementing continuousely.How can I implement this properly?
I also want to know how to draw these array objects using spriteBatch.
It will be very helpful if I get an explanation with code.

In order to keep the size of the birds array constant you need to modify your MakeBird() function into this
private void MakeBird() {
int birdsToAdd = 10 - birds.length;
for (int i = 0; i < birdsToAdd ; i++) {
birds.add(objectFactory.createBird());
}
}
Your version of MakeBird() would simply add the same object ten times into the birds array. So, why is this bad? Because Java objects are reference type, meaning that if you modify one object all the references to that object will also change. In your case, if you change the position of one bird in the array, all the birds position will change. I don't know if this is intentional. But if you want to add different Bird objects then you should use the MakeBird() function like I implemented above. Nice catch there by marius.
As for your second query about how to render these objects to the screen, what you can do is, update your Bird class to contain a render or a draw method (you can name the method whatever you want) and in your main class you should update your render method to call the render/draw method of the Bird class. You can do this as such
//Bird class
class Bird {
//Your data members
//Your member functions
public void draw(SpriteBatch batch) {
batch.draw(some_bird_image, bird_x_position, bird_y_position);
}
}
//Render method in main class
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
for(Bird bird : birds) {
bird.draw(batch);
}
batch.end();
}

You have to create a new Bird object every for-loop run. So write birds.add(objectFactory.createBird()) instead of birds.add(b). I think that should fix the first error.
You somehow need to implement the following like it is decribed here:
public class Game implements ApplicationAdapter {
private SpriteBatch batch;
private Texture texture;
public void create () {
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("example.png"));
}
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // This cryptic line clears the screen.
batch.begin();
batch.draw(texture, 10, 10);
batch.end();
}
}
But I really recommend you to use Box2D, because it is very ease to use and you can find plenty of tutorials for it.

Related

How to getChildAt a movie clip inside a movieclip?

I have a movie clip linkage named "trainglePoint" inside a movieclip with a "bgdemo" instance and I was wondering how will I get the "trianglePoint" to work.
also "gags" is the character that will get the "trianglePoint"
this is my code below, which doesn't work.
thanks!
edit: forgot to add that the 'trianglePoint' is put onto stage multiple times (dont know if that helps)
var pickUpsArray:Array = new Array();
stage.addEventListener (Event.ENTER_FRAME, pickUpItems);
public function pickUpItems (e:Event)
{
for (var i=0; i<numChildren;i++)
{
if (getChildAt(i) is bgdemo.trianglePoint)
{
pickUpsArray.push(getChildAt(i));
}
}
for (var j=0; j<pickUpsArray.length;j++)
{
if (gags.hitTestObject (pickUpsArray[j]))
{
removeChild(pickUpsArray[j]);
trace ("hitTestObject: YES");
}
}
}
You should instead use GetChildByName(name).
GetChildAt implies that you know where in the child layers the child you want is located.
So if your child is named "trianglePoint" and you want to access it, just use getChildByName("trianglePoint");
There are several problems with the rest of your code.
First, you push into an array but never remove from it. Using removechild does not remove the object from the array which means you will always test it for collision with "gags".
The other issue is that you are adding your object to the array in each frame. What you should do instead is get your object from GetChildByName, push it into the array THEN loop and interact with it.
I would do it like this:
import flash.display.MovieClip
public class bgdemo extends MovieClip
{
var pickUpsArray:Array
public function bgdemo()//constructor for your parent movieclip
{
pickUpsArray = new Array();
this.addEventListener (Event.ADDED_TO_STAGE, init_ok);
//this is to ensure the parent movieclip (bgdemo) is on the stage and we can access its children.
}
private function init_ok(e:Event):void
{
this.removeEventListener(Event.ADDED_TO_STAGE, init_ok);
//removing the listener so that we only do this once
pickUpsArray.push(this.getChildByName("trianglePoint"));
stage.addEventListener (Event.ENTER_FRAME, pickUpItems);
}
private function pickUpItems (e:Event):void
{
for (var j=0; j<pickUpsArray.length;j++)
{
if (gags.hitTestObject (pickUpsArray[j]))
{
removeChild(pickUpsArray[j]);
pickUpsArray.splice(j, 1); //removing the object from the array so we can't collide with it anymore
trace ("hitTestObject: YES");
}
}
}
}
You create a new Array in your bgdemo movieclip's constructor and add a listener to the fact that the movieclip is added to the stage. When the movieclip is added to the stage, (remove the "added to stage listener"), get your child, add it to the array and add a listener to enter frame.
On each frame you then test every object in your array (only 1 at this point) and "gags". If gags and the object collide, you then remove the object from both the display list and the array.
Try that and tell me if that works (the way you want it to work). If it's not what you wanted, then please be more precise on your original post ;)

AS3- how to addchild this array(john) correctly

I do not know how to add the john array and make a hittestobject with it.
Bal is a different class non relevant to this problem.
I've tried to do john[new Bal]
tried john[ k ]
tried z and to specify z as a for-loop but then i would just get Z balls place.
This is supposed to become a space-invader type of game. I'm trying to make a hit test object between HENK and the 'falling balls' (JOHN). I do not know how to work with arrays especially given the fact that is should be timer-triggered.
Thanks
public class Main extends Sprite
{
public var henk:Sprite = new Sprite();
public var level:Timer = new Timer (2000, 0);
public var valTijd:Number = new Number
public var i:Number = 2000;
public var john:Array = new Array();
public var k:Number = 9000;
public function Main():void
{
henk.graphics.beginFill(0xFF00FF);
henk.graphics.drawCircle(0, 500, 20);
henk.graphics.endFill();
addChild(henk);
level.addEventListener(TimerEvent.TIMER, up);
level.start();
henk.addEventListener(Event.ENTER_FRAME, muis);
henk.addEventListener(Event.ENTER_FRAME, hit);
}
public function up(e:TimerEvent):void
{
var tijdje:Timer = new Timer( i, 0)
tijdje.addEventListener(TimerEvent.TIMER, tijdLuisteraar);
tijdje.start();
i = i - 250;
}
public function muis (e:Event):void
{
henk.x = mouseX;
}
public function hit (e:Event): void
{
if ( henk.hitTestObject(john [k] ))
{
if (contains(john[k] ))
{
removeChild(henk);
}
}
}
public function tijdLuisteraar(e:TimerEvent):void
{
john.push(new Bal);
addChild(john[k]);
}
}
}
welcome to stackoverflow!
This problem is actually fairly simple, I will describe how you will probably want to use an array in the case you described.
At the part where you create new Balls you want to append them to an array, which will be something like the following:
var ball = new Bal();
john.push(ball);
addChild(ball);
This will go inside your timer-triggered function, obviously.
Secondly, you want to have a hitTestObject with henk and all of the balls stored in the john array.
for(var i = 0; i < john.length; i++) {
if (henk.hitTestObject(john[i])) {
// well, that's a bummer for your player, henk hit one of the balls in the john array
// display something like a message here
}
}
This will automatically detect the size of the array, so all elements are tested. Be careful with hitTestObject when you have a lot of elements in the john-array, this can slow down your game drastically.
Furthermore, reflecting your code I suggest the following:
remove public var i:Number = 2000; and public var k:Number = 9000;, these have no meaning anymore
use a mouse event to move your henk object, not an ENTER_FRAME. I guess you will be able to find how this works. This will only trigger the function when it has to do something, resulting in less CPU-power needed and a cleaner code.
if you want to make the game even cooler, you could add the support for using the arrow keys

Actionscript 3 eventlisteners, hittestobject and arrays of custom objects

Another desperately stuck first year student here. And I have to use FlashCS as my coding environment. And it sucks. So I'll try some well constructed and clear questions. There is:
public var object: symbol1;
public var objectarray: Array = new Array();
in my main. Then a function there that uses a timer and spawns a new object and pushes it onto the array:
object = new symbol1;
objectarray.push(object)
but then when I trace() the .length of the array it displays TWO numbers of the array length every timer period in the output. As in:
1 1 2 2 3 3
etc. This is my first mystery. Why two not one? because there is no way I'm calling the function that includes the trace () twice. Also I think I need to be able to remove my object from the objectarray when it goes off the stage, but the objectarray.pop() doesn't seem to work if I use it like so in a function:
if (object.y == stage.stageHeight)
objectarray.pop()
As in I try trace() the array.length before and after the .pop(), but it just keeps going up by one every timer period.
And the other, bigger issue is I want to know if you are allowed to put the .addEventListeners that you usually place right under the main function of any class into a statement loop. As in I've got
class extends Main {
class function() {
for (var i:Number = 0; i < objectarray.length; i++){
objectarray[i].addEventListener(Event.ENTER_FRAME, collision);}}
And then, if it is allowed, the program doesn't seem to enter the collision function of this same class anyway.
function collision (event:Event) : void{
if (this.hitTestObject(object)){
trace('hit');}}
so I searched and ended up adding a
var clip:MovieClip = MovieClip(e.target);
in the first line of the function, but then it didn't work and I realized I on't understand what's it meant to do, what's going on anymore and what is the syntax for this casting.
Thank you very much.
Edit/Update: adding more of my code eventhough I hate copypasting it like this. This is the symbol class that is going to change when an object of another class hits it
public class Head extends Main {
public function Head(){
this.stop();
for (var i:Number = 0; i < nicesnowflakearray.length; i++){
nicesnowflakearray[i].addEventListener(Event.ENTER_FRAME, snowhit);
}
}
public function snowhit(event:Event) : void {
if (this.hitTestObject(nicesnowflake)){
//I changed this line to (e.currentTarget.hitTestObject(nicesnowflake)) as Atriace suggested, but nothing changed, and I just don't understand why my version wouldn't work.
trace("hit");
}
}
And this is the class that spawns the objects that are supposed to hit the Head object:
public class Main extends MovieClip {
public var nicesnowflake: fallingsnow;
var nicesnowflakespawntimer: Timer = new Timer(1000);
public var nicesnowflakearray: Array = new Array();
public function Main() {
nicesnowflakespawntimer.addEventListener(TimerEvent.TIMER, nicesnowflakespawn);
nicesnowflakespawntimer.start();
}
public function nicesnowflakespawn(event:TimerEvent) : void {
nicesnowflake = new fallingsnow;
nicesnowflake.x = Math.random()* stage.stageWidth;
nicesnowflake.y = - stage.stageHeight + 100;
nicesnowflakearray.push(nicesnowflake);
stage.addChild(nicesnowflake);
trace(nicesnowflakearray.length);
}
Why two, not one?
Anytime you extend another class, it implicitly calls the parent class' constructor. We know this as super(), and can be quite handy. This is why you're getting doubles on your trace statement. Technically, you can choose not to call super().
.pop()
It should remove the last element from that array, however, I'm thinking that if an arbitrary object leaves the stage, you can't be gauranteed it'll be the last element. Ergo, you want splice()
if (object.y == stage.stageHeight) {
objectarray.splice(objectarray.indexOf(object), 1)
}
Event Listeners
I didn't follow your quandary, so I'll just try to rewrite what I think you were trying to do.
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
private var objectarray:Array = []; // Note, I haven't populated it with anything, I'm assuming you have.
private var theWall:MovieClip = new MovieClip(); // I haven't added this to the stage, or given it shape. You need to for hitTestObject to work.
public function Main() {
// This is your constructor.
for (var i:Number = 0; i < objectarray.length; i++) {
objectarray[i].addEventListener(Event.ENTER_FRAME, collision);
}
}
private function collision(e:Event):void {
if (e.currentTarget.hitTestObject(theWall)) {
trace('hit');
}
}
}
}
Of note, you may want to look at a guide to hitTestObject() if it's giving you issues.

having issues working with movie clips inside of an array [as3]

In a game I'm making, I'm trying to have sprites put on top of a hit box. The best way I could think of accomplishing this is to make two arrays; one for the hit boxes, and one for the sprites, and then have the sprites stay on top of their respective hit-box via a for loop that will be in its own script.
Problem is, is that when I try to get the MovieClips in either of the arrays to do anything, it doesn't work. If I do a trace on the X or Y location of the sprites, I get "undefined" in my terminal. I'm going to explain this from the top down.
Here is an excerpt from my class that contains the for loop (Dasengine is my main class fyi)
for(var i:Number = 0; i < Dasengine.ovrcnt.length; i++){
trace(Dasengine.ovrcnt[i].x); //returns "undefined"
trace(Dasengine.ovrcnt[i]); //returns "object Onmea"
Dasengine.ovrcnt[i].x = Dasengine.enemycnt[i].x;//this isn't working
}
In another script when an enemy spawns, they go through this method.
if(ENEMY SPAWN CONDITION IS MET ){
Dasengine.baddie = new nme_spawn.Enemya(); //ENEMY HITBOX
Dasengine.Obaddie = new nme_overlay.Onmea(); //ENEMY's sprite
Dasengine.enemycnt[cntup] = [Dasengine.baddie]; //Enemy's Hit box movie clip is put in array meant for holding enemy movie clips
Dasengine.ovrcnt[cntup] = [Dasengine.Obaddie]; //Enemy sprites that go over the hit boxes are stored here
cntup++; //this is so movie clips are put in new parts of the array
}
in my main class, the movie clips are declared as follows also I have the addChild functionality in there.
public static var Obaddie:nme_overlay.Onmea;
//^variable for sprite
public static var baddie:nme_spawn.Enemya;
//^variable for hitbox
also Obaddie= Overlay baddie. Its the MovieClip that acts as what goes on top of the hitbox, this is what the player will see
badde = is simply the hitbox. This contains the "meat" of the enemy ai.
I talked about this with some friends and they thought I might need to define what 'X' is inside of the class of the object that is in the array. So I did the following
package nme_overlay {
import flash.display.*;
import flash.events.*;
import nme_spawn.*;
public class Onmea extends MovieClip{
// Constants:
// Public Properties:
// Private Properties:
public static var xloc:int = 0;
// Initialization:
public function Onmea() {
this.addEventListener(Event.ENTER_FRAME, overlaya);
}
private function overlaya(e:Event){
xloc = 55;
//trace(xloc);
}
}
}
and then for the looping class I did this
for(var i:Number = 0; i < Dasengine.enemycnt.length; i++){
trace(Dasengine.ovrcnt[i]);//returns "object Onmea"
trace(Dasengine.ovrcnt[i].xloc);//still returns "undefined"
}
Your xloc variable is static--it belongs to nme_overlay, the Class, not any particular instance. If you were to do this in your code AND you had strict mode on (which I suspect you do not, because there's a lot of stuff in your code that should be giving you at least warnings), you'd get an error that would tell you exactly that:
for(var i:Number = 0; i < Dasengine.enemycnt.length; i++){
trace(Dasengine.ovrcnt[i]);//returns "object Onmea"
trace(nme_overlay(Dasengine.ovrcnt[i]).xloc);//still returns "undefined"
}

How to share an Array between all Classes in an application?

I want to share an Array which all classes can "get" and "change" data inside that array. Something like a Global array or Multi Access array. How this is possible with ActionScript 3.0 ?
There are a couple of ways to solve this. One is to use a global variable (as suggested in unkiwii's answer) but that's not a very common approach in ActionScript. More common approaches are:
Class variable (static variable)
Create a class called DataModel or similar, and define an array variable on that class as static:
public class DataModel {
public static var myArray : Array = [];
}
You can then access this from any part in your application using DataModel.myArray. This is rarely a great solution because (like global variables) there is no way for one part of your application to know when the content of the array is modified by another part of the application. This means that even if your data entry GUI adds an object to the array, your data list GUI will not know to show the new data, unless you implement some other way of telling it to redraw.
Singleton wrapping array
Another way is to create a class called ArraySingleton, which wraps the actual array and provides access methods to it, and an instance of which can be accessed using the very common singleton pattern of keeping the single instance in a static variable.
public class ArraySingleton {
private var _array : Array;
private static var _instance : ArraySingleton;
public static function get INSTANCE() : ArraySingleton {
if (!_instance)
_instance = new ArraySingleton();
return _instance;
}
public function ArraySingleton() {
_array = [];
}
public function get length() : uint {
return _array.length;
}
public function push(object : *) : void {
_array.push(object);
}
public function itemAt(idx : uint) : * {
return _array[idx];
}
}
This class wraps an array, and a single instance can be accessed through ArraySingleton.INSTANCE. This means that you can do:
var arr : ArraySingleton = ArraySingleton.INSTANCE;
arr.push('a');
arr.push('b');
trace(arr.length); // traces '2'
trace(arr.itemAt(0)); // trace 'a'
The great benefit of this is that you can dispatch events when items are added or when the array is modified in any other way, so that all parts of your application can be notified of such changes. You will likely want to expand on the example above by implementing more array-like interfaces, like pop(), shift(), unshift() et c.
Dependency injection
A common pattern in large-scale application development is called dependency injection, and basically means that by marking your class in some way (AS3 meta-data is often used) you can signal that the framework should "inject" a reference into that class. That way, the class doesn't need to care about where the reference is coming from, but the framework will make sure that it's there.
A very popular DI framework for AS3 is Robotlegs.
NOTE: I discourage the use of Global Variables!
But here is your answer
You can go to your default package and create a file with the same name of your global variable and set the global variable public:
//File: GlobalArray.as
package {
public var GlobalArray:Array = [];
}
And that's it! You have a global variable. You can acces from your code (from anywhere) like this:
function DoSomething() {
GlobalArray.push(new Object());
GlobalArray.pop();
for each (var object:* in GlobalArray) {
//...
}
}
As this question was linked recently I would add something also. I was proposed to use singleton ages ago and resigned on using it as soon as I realized how namespaces and references work and that having everything based on global variables is bad idea.
Aternative
Note this is just a showcase and I do not advice you to use such approach all over the place.
As for alternative to singleton you could have:
public class Global {
public static const myArray:Alternative = new Alternative();
}
and use it almost like singleton:
var ga:Alternative = Global.myArray;
ga.e.addEventListener(GDataEvent.NEW_DATA, onNewData);
ga.e.addEventListener(GDataEvent.DATA_CHANGE, onDataChange);
ga.push(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "ten");
trace(ga[5]); // 5
And your Alternative.as would look similar to singleton one:
package adnss.projects.tchqs
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class Alternative extends Proxy
{
private var _data:Array = [];
private var _events:AltEventDisp = new AltEventDisp();
private var _dispatching:Boolean = false;
public var blockCircularChange:Boolean = true;
public function Alternative() {}
override flash_proxy function getProperty(id:*):* {var i:int = id;
return _data[i += (i < 0) ? _data.length : 0];
//return _data[id]; //version without anal item access - var i:int could be removed.
}
override flash_proxy function setProperty(id:*, value:*):void { var i:int = id;
if (_dispatching) { throw new Error("You cannot set data while DATA_CHANGE event is dipatching"); return; }
i += (i < 0) ? _data.length : 0;
if (i > 9 ) { throw new Error ("You can override only first 10 items without using push."); return;}
_data[i] = value;
if (blockCircularChange) _dispatching = true;
_events.dispatchEvent(new GDataEvent(GDataEvent.DATA_CHANGE, i));
_dispatching = false;
}
public function push(...rest) {
var c:uint = -_data.length + _data.push.apply(null, rest);
_events.dispatchEvent(new GDataEvent(GDataEvent.NEW_DATA, _data.length - c, c));
}
public function get length():uint { return _data.length; }
public function get e():AltEventDisp { return _events; }
public function toString():String { return String(_data); }
}
}
import flash.events.EventDispatcher;
/**
* Dispatched after data at existing index is replaced.
* #eventType adnss.projects.tchqs.GDataEvent
*/
[Event(name = "dataChange", type = "adnss.projects.tchqs.GDataEvent")]
/**
* Dispatched after new data is pushed intwo array.
* #eventType adnss.projects.tchqs.GDataEvent
*/
[Event(name = "newData", type = "adnss.projects.tchqs.GDataEvent")]
class AltEventDisp extends EventDispatcher { }
The only difference form Singleton is that you can actually have multiple instances of this class so you can reuse it like this:
public class Global {
public static const myArray:Alternative = new Alternative();
public static const myArray2:Alternative = new Alternative();
}
to have two separated global arrays or even us it as instance variable at the same time.
Note
Wrapping array like this an using methods like myArray.get(x) or myArray[x] is obviously slower than accessing raw array (see all additional steps we are taking at setProperty).
public static const staticArray:Array = [1,2,3];
On the other hand you don't have any control over this. And the content of the array can be changed form anywhere.
Caution about events
I would have to add that if you want to involve events in accessing data that way you should be careful. As with every sharp blade it's easy to get cut.
For example consider what happens when you do this this:
private function onDataChange(e:GDataEvent):void {
trace("dataChanged at:", e.id, "to", Global.myArray[e.id]);
Global.myArray[e.id]++;
trace("new onDataChange is called before function exits");
}
The function is called after data in array was changed and inside that function you changing the data again. Basically it's similar to doing something like this:
function f(x:Number) {
f(++x);
}
You can see what happens in such case if you toggle myArray.blockCircularChange. Sometimes you would intentionally want to have such recursion but it is likely that you will do it "by accident". Unfortunately flash will suddenly stop such events dispatching without even telling you why and this could be confusing.
Download full example here
Why using global variables is bad in most scenarios?
I guess there is many info about that all over the internet but to be complete I will add simple example.
Consider you have in your app some view where you display some text, or graphics, or most likely game content. Say you have chess game. Mayby you have separated logic and graphics in two classes but you want both to operate on the same pawns. So you create your Global.pawns variable and use that in both Grahpics and Logic class.
Everything is randy-dandy and works flawlessly. Now You come with the great idea - add option for user to play two matches at once or even more. All you have to do is to create another instance of your match... right?
Well you are doomed at this point because, every single instance of your class will use the same Global.pawns array. You not only have this variable global but also you have limited yourself to use only single instance of each class that use this variable :/
So before you use any global variables, just think twice if the thing you want to store in it is really global and universal across your entire app.

Resources