Swift - create array of class objects - arrays

I am trying to create an array that will store my class objects. The createEnemies method is called when a level is started. Which should then create the enemy objects. However I don't understand how to do that. It should be created after "if(levelNumber < 5)"
class level {
class func createEnemies() {
numEnemies = Int(floor(levelNumber * 1.5 + 10))
println("Number of Enemies this level: \(numEnemies)")
if(levelNumber < 5){
//Create numEnemies amount of class objects
}
}
}
//Enemy Variables
var enemiesKilled = 0
class enemy {
class func enemiesKilled() {
}
class standard {
var health:Int = 10
var name:String = "Standard"
var worth:Int = 10
var power:Int = 10
init () {
}
func kill() {
}
func damage(damage: Int) {
self.health -= damage
println("\(self.name) was damaged \(damage)")
if(self.health <= 0){
self.kill()
}
}
}

For Swift 3.1
var enemies:[enemy] = [enemy]()

Create an array of elements of a custom class like this:
var enemies = [enemy]()
You can add elements to it like this:
enemies.append(anEnemy: enemy)

If you want to have a specific number of enemies in the array there are several ways to achieve this (I write Enemy instead of enemy because the first letter of a class name is usually uppercase):
// "old fashioned" for loop
var enemies = [Enemy]()
for _ in 1...numEnemies {
// call initializer of Enemy
enemies.append(Enemy())
}
// my personal preference (Range has a method named map which does the same as Array)
// without the "_" you could also access the numbers if you want
let enemies = (1...numElements).map{ _ in Enemy() }
If you need to access the array later on you should declare the variable under your comment //Enemy Variables.

Related

How to update an array passed as a parameter on init when new items are added on the original array

Say for example I have an array that needs to be used on multiple classes, if I need a worker to update the values of this array how do I make it so that when I add new values to the said array the object worker's value also changes.
Example:
class Object {
var id: Int
var foo: String
var bar: Int
init(id: Int, foo: String, bar: Int) {
self.id = id
self.foo = foo
self.bar = bar
}
}
class ObjectWorker {
var objects: [Object]
init(objects: [Object]) {
self.objects = objects
}
func updateObjects(withId id: Int) {
self.objects.forEach { $0.foo = "a different value" }
}
}
class SomeClass {
// this declaration will happen on more than one class
var objects: [Object] = ... // let's just say there are 10 objects here
lazy var worker = ObjectWorker(objects: self.objects)
init() {
// to initialize the workers
_ = worker
print(objects.count) // 10
print(worker.objects.count) // 10
let newObjects: [Object] = ... // let's say this has 5 new values
objects.append(contentsOf: newObjects)
print(objects.count) // 15
print(worker.objects.count) // 10
}
}
I have tried making the ObjectWorker's init be an inout parameter like this init(objects: inout [Object] but even then the result is still the same. The updateObjects works though even if the init is not an inout parameter.
Note:
I know I can do this by using the ObjectWorker as the container of the objects instead of what is currently going on in here, but is there a way to do this without doing that?
I can also use static functions instead, but let's not go there
As already mentioned, Swift Arrays are value types so ObjectWorker gets a copy of the array. If you don't want to use ObjectWorker as a container, you could use an NSArray instead (which is a reference type).

Adding objects to a static array in swift

Is it possible to add subclassed objects to a static array inside the parent class? I would like to run a function on all instances created. Another SO question describes being able to loop over an array to call a function on each instance and I think this is the end result I would like as well but my problem is even getting the instances into a static array that keeps track of all instances.
Of course my code is more modular but here is a simplified view of the code and hierarchy:
class Jungle {
static var jungle: [Animals] = []
}
class Tigers: Animals {
static var tigerPopulation: Int = 0
override init(name:String){
super.init(name: name)
Tigers.tigerPopulation += 1
}
deinit {
Tigers.tigerPopulation -= 1
}
}
class Monkeys: Animals {
static var monkeysPopulation: Int = 0
override init(name: String){
super.init(name: name)
Monkeys.monkeysPopulation += 1
}
deinit {
Monkeys.monkeysPopulation -= 1
}
}
class Snakes: Animals {
static var snakePopulation: Int = 0
override init(name: String){
super.init(name: name)
Snakes.snakePopulation += 1
}
deinit {
Snakes.snakePopulation -= 1
}
}
I get the feeling that I should have created the Jungle class first so they all would subclass from Jungle I guess but I'm still stumped on how I would get the instances into an array.
Instead of trying to trigger some behavior to increase the monkey count, tiger count etc from within an initializer, it will be easer to just add everything to an animals array, and then use the animals array to figure out how many Tigers/Snakes/Monkeys there are.
Basing the count off that single array is going to be easier in the long term than having a bunch of different variables, and making sure they're in sync (e.g. by decreasing during deinit).
Here's a minor reshuffle of your jungle, animal, and animal subclasses:
class Jungle {
var animals: [Animal] = []
func addAnimal(animal: Animal) {
animals.append(animal)
}
// this is a computed property, everytime you access it
// via jungle.tigerPopulation, the code inside this statement
// executes, and you get the value that is returned
var tigerPopulation: Int {
// tigers is a new array containing only tigers
let tigers = animals.filter{ animal in
return animal is Tiger
}
// return the number of items in the tigers array
return tigers.count
}
}
class Animal {
let name: String
init(name: String) {
self.name = name
}
func sayHello() {
print("I'm an animal")
}
}
class Tiger: Animal {
// tiger specific stuff
override func sayHello() {
print("I'm a tiger called", name)
}
}
class Monkey: Animal {
// monkey specific stuff
}
class Snake: Animal {
// snake specific stuff
override func sayHello() {
print("I'm a snake called", name)
}
}
Something to point out is that because each of the animal subclasses are currently doing the same thing (assigning the name) you don't need to override the initializer in the subclasses - that's the value of class inheritance!
You can override the subclasses sometimes in the future if an animal has to do something that's different from other animals.
The most interesting thing that's happening here is the computed property that calculates the tiger population by filtering the tigers in the animals array, and returning the count, this way you're not manually +/- 1 the population count, but just calculating it whenever you ask for it.
Then you'd use your jungle object something like this:
let jungle = Jungle()
// jungle.animals -> []
// jungle.animals.count -> 0
// jungle.tigerPopulation -> 0
jungle.addAnimal(animal: Tiger(name: "Tony"))
// jungle.animals -> [Tiger()]
// jungle.animals.count -> 1
// jungle.tigerPopulation -> 1
jungle.addAnimal(animal: Snake(name: "Susan"))
// jungle.animals -> [Tiger(), Snake()]
// jungle.animals.count -> 2
// jungle.tigerPopulation -> 1
for animal in jungle.animals {
animal.sayHello()
}
// prints ->
// I'm a tiger called Tony
// I'm a snake called Susan

Variable/class in array

So i'm kinda stuck on somethng.
i kinda want a random drop wen i kill my monster.
this is what i got.
i've got a weapon1
class weapon {
var str = Int
}
class Weapon1: weapon {
override init() {
super.init()
str = 10
}
class Weapon2: weapon {
override init() {
super.init()
str = 20
}
i've got a monster
class monster {
str = 20
def = 10
drops = [weapon1(), Weapon2()]
}
and i have a place to link my drops to if my monster is death
var itemdrops = weapon()
and when my monster dies it should link my monster drop to my itemdrops..
so i can use it somewhere like in an inventory.
but when i try to do it like this
random = (arc4random_uniform(UInt32(monster.items.count-1)))
//-1 becous it would say 2 items and an array starts at 0
itemdrops = monster.items[random]
but then items is in white..
The problem is that items is an instance property of monster:
class monster {
str = 20
def = 10
drops = [weapon1(), Weapon2()]
}
But when you try to access it, you try to access it through the class:
monster.items[random]
The class monster has no class/static property items, so your code cannot compile.
This is a good reason for using capital letters for class names, as you are supposed to. It helps you keep track of whether this thing is a class or an instance. Example:
class Monster {
str = 20
def = 10
drops = [Weapon1(), Weapon2()] // assume you've named these classes correctly
}
Now, in your code, you would say:
let aMonster = Monster() // an _instance_
var itemdrops = aMonster.items[random]

AS3 remove all instances of an object?

I have a game with in AS3 with a document class and a custom class which is attached to a movieclip in my .fla. Instances of that object are made multiple times/second. I want to delete those instances when, let's say, there are 100 of them. (because of performance problems after a while) The instances are stored in an Array after they are made.
You can remove them by using this.removeChild(obj); and obj is your object from array. So what you need is to loop through array and remove them.
That will remove all objects when objects are over 100.
if(array.length > 100)
{
for(var i:int = array.length - 1; i > -1; i--)
{
stage.removeChild(array[i]);// or any other parent containing instances
array.pop();
//array[i] = null; // if you want to make them null instead of deleting from array
}
}
Tip: Negative Loop (i--) is faster in performance than Positive Loop (i++).
Tip: pop() is faster in performance than unshift().
Update:
That will remove objects only if they are over 100, resulting in only 100 last objects remain on stage.
if(array.length > 100)
{
for(var i:int = array.length - 1; i > -1; i--)
{
if(array.length > 100)
{
stage.removeChild(array[i]);// or any other parent containing instances
array.unshift();// if you want to delete oldest objects, you must use unshift(), instead of pop(), which deletes newer objects
//array[i] = null; // if you want to make them null instead of deleting from array
}
}
/****** MyClass.as *********/
public class MyClass extends Sprite{
private var myVar:int=8567;
private function myClass():void{
//blablabla
}
public class destroy():void{
myVar = null;
this.removeFromParent(true); //method of Starling framework
}
}
/******** Main.as ****************/
public var myInstance:MyClass = new Myclass();
//Oh!! i need remove this instance D:
myInstance.destroy();

Moving objects in array

I have an array which is filled with platforms that are supposed to move.
var MovingPlatformArray:Array = new Array();
for (var c:int = numChildren - 1; c >= 0; c--){
var child3:DisplayObject = getChildAt(c);
if (child3.name == "movingplatform"){
MovingPlatformArray.push(child3);
}
}
this.addEventListener(Event.ENTER_FRAME,ctrl_birdie);
function ctrl_birdie(e:Event):void{
for(var c in MovingPlatformArray){
MovingPlatform[c].y += speed;
if(MovingPlatformArray[c].hitTestPoint(birdie.x,birdie.y,true)){
birdtelleryvertrager=0;
birdtellery = 0;
birdie.y-=14;
}
if(movingplatform.y <= 25){
speed = 2;
}
if(movingplatform.y >= 350){
speed = -2;
}
}
Right now I have 2 moving platforms in this array. But only one moves up and down. But they both register a touch with the birdie. Am I doing something wrong?
In your listener, you're only setting the position of one platform, which ever one "movingplatform" is a reference to. As all your stage instances of moving platforms are named "movingplatform", one lucky platform is getting referenced by name (the rest ignored), instead of what you intended, which is to use the references in your array and adjust each platform.
You probably meant for movingplatform to be a local variable in your event handler, declared something like this:
var movingplatform:DisplayObject = MovingPlatformArray[c] as DisplayObject;
I'd recommend using a for each loop in place of the for in, because I think it's a little cleaner, but this is a minor style thing:
for each (var platform:DisplayObject in MovingPlatformArray)
{
platform.y += speed;
... rest of your code ...
}
For the sake of clarity, I edited the loop variable to be platform instead of movingplatform, to avoid confusion of having a local variable shadow a stage instance (i.e. this.movingplatform). I wanted it to be clear that the stage instance name is not being used here, because the unintentional instance name reference in your code is the source of your problem in the first place.
As far as i'm concerned, you have two options. use a for each, as adam smith suggested or use a for-loop as it was intended to be used :)
for(var c:uint = 0; c < MovingPlatformArray.length; c++){...
and btw: should "MovingPlatform[c].y += speed;" not be "MovingPlatformArray[c].y += speed;"?
edit: looking at your code, i would also suggest you use MovingPlatformArray[c].hitTestObject(birdie) instead of MovingPlatformArray[c].hitTestPoint(birdie.x,birdie.y,true)
If I were you, I would bring the logic for the platform out, and store it in a class. (Ideally you would do this for the birdie object as well). I have created an example below. The movieclips on the stage should extend Platform rather than MovieClip so they invoke the methods at the bottom.
// Use vectors if you know all the items are going to be the same type
var platforms:Vector.<Platform> = new <Platform>[];
for (var c:int = numChildren - 1; c >= 0; c--){
var child:DisplayObject = getChildAt(c);
// You shouldn't check against names (as per the original post). Because
// names should be unique
if (child is Platform){
platforms.push(child);
// This could be random so each platform has a different range
// This means platform 1 could go from y 30 to y 400, platform 2
// could go from y 60 to y 200, etc
child.setRange(25, 400);
}
}
this.addEventListener(Event.ENTER_FRAME, gameLoop);
// Have an overall game loop
function gameLoop(e:Event):void {
// Loop over the platforms
platforms.forEach(function(item:Platform, i:int, a:Vector.<Platform>):void {
// Hit test function in the class means you only have to pass in one mc
// rather than the points and a boolean
if(item.hitTest(birdie)) {
birdtelleryvertrager=0;
birdtellery = 0;
birdie.y-=14;
}
// Removed the movement logic, this should be kept out of the game loop
// plus how much better does this read?
item.move();
});
}
Then in a class location somewhere, like in a folder game/activeObjects
// A class for the platform stored else where
package game.activeObjects
{
import flash.display.MovieClip;
/**
*
*/
public class Platform extends MovieClip {
private const SPEED:Number = 2;
private var _direction:int = 1;
private var _minimumHeight:Number = 25;
private var _maximumHeight:Number = 350;
public function Platform() {
}
public function setRange(minimumHeight:Number, maximumHeight:Number) {
_minimumHeight = minimumHeight;
_maximumHeight = maximumHeight;
}
public function move():void {
this.y += SPEED * _direction;
if(this.y <= _minimumHeight) {
_direction = 1;
} else if(this.y >= _maximumHeight) {
_direction = -1;
}
}
public function hitTest(mc:MovieClip):Boolean {
return hitTestPoint(mc.x,mc.y,true);
}
}
}

Resources