GMS2, when destroying a child, all children get affected. Parent object not functioning as intended - game-maker-studio-2

I am having an issue with my parent object and its children.
My intention is to have an enemy spawner (child) that functions independently of other children spawners . The spawner will spawn enemies until the desired amount is reached and then be destroyed (child is destroyed). However, when I destroy an enemy spawned by my parent spawner object, all children spawners spawn an enemy instead. Example. I have two children spawnwers that spawn an enemy. When I destroy from child A, child A and B both spawn an enemy. Furthermore, when I destroy the required amount of enemies from the spawner, both A and B child spawners are destroyed except for just A child.
Below is code.
///PARENT SPAWNER OBJECT
PARENT STEP EVENT
if(enemy_dead_count <= 0){
sprite_index = sDestroyed_Ghost_Spawner;
if (sprite_index == sDestroyed_Ghost_Spawner){
if(image_index >= 9 && image_index < 10){
image_index = 9;
}
}
} else {
while(spawn_ghost == 0){
instance_create_layer(x,y+32,"Player",oEnemy_spawner_animation);
spawn_ghost += 1;
}
}
//PARENT CREATE EVENT
spawn_ghost = 0;
enemy_dead_count = 3;
The object, oEnemy_spawner_animation, is an object that scales its xscale and yscale and when reaches a desired size changes the instance to oEnemy_ghost
//ANIMATION CODE BELOW
//animator create event
image_xscale = 0;
image_yscale = 0;
//animator step event
if image_xscale <= 1 {
image_xscale += 0.008;
image_yscale = image_xscale;
} else{
image_xscale = 1;
//update yscale
image_yscale = image_xscale;
instance_change(oEnemy_ghost,true);
}
In the Enemy Parent , I wont post all the code as it is quite extensive but ill post the relevant
if(hp<=0){
instance_destroy();
with(x,y,oEnemy_ghost_spawner){
spawn_ghost = 0;
enemy_dead_count -= 1;
}
}
Above, when an enemy is destroyed, I use the with statement to spawn a new enemy.
I am targeting the child here instead of the parent , perhaps that's the problem?
I have a feeling I need to somehow tie the enemy spawned ID to the child spawner ID, but I do not know how to achieve this.
Any thoughts on how to achieve my intended functionality? How do I make my children spawners function independently of eachother?

With the with() function, you'll go through every object of that type. I think if that includes a parent, it'll affect the children as well.
A possible solution could be to:
Make another child of the current 'parent' function, and use the current parent as just a base of the enemy spawner that'll affect all enemy spawners, and not an entity of itself.
make a boolean that'll only be set to true for the parent (like isParent), and set it to false for it's children.
if you want to make each spawner unique of each other, then you could try giving them an unique ID, and inside the with() function, search for that unique ID if it's the one you're looking for.

Related

AMCharts - Dynamically add children to tree map

I am thoroughly enjoying AMChart's many features but I couldn't find any way to dynamically add some children to a treemap.
I am trying to load additional children on "hit" for each element
for (var i = 0; i < this.maxDepthLevel; i++) {
const series = this.chart.seriesTemplates.create(i);
series.columns.template.events.on("hit", async function(ev) {
const data = ev.target.dataItem.dataContext;
children = await api.getChildrenOf(data.id);
ev.target.dataItem.treeMapDataItem.children.values.push(...children);
});
}
^ this doesn't work and when doing this and then zooming out, I get
I even tried changing the underlying data and then calling
this.chart.invalidateRawData();
but to no avail.
Does anyone have any experience with adding such dynamic children to a tree map?
I cannot simply load everything upfront, there are far too many possible layers of depth unfortunately and the request will be too large!
Rather than directly pushing child items to children values you need to do it like this :
for(var index=0; index < children.length; index++){
var newChildDataItem = new am4charts.TreeMapDataItem();
newChildDataItem.value = children[index].value;
newChildDataItem.name = children[index].name;
newChildDataItem.color = ev.target.dataItem.dataContext.color;
ev.target.dataItem.dataContext.children.insert(newChildDataItem);
}

(GMS2) Spawner object doesn't spawn items

In GMS2 I have a spawner item with the following code:
In the create event:
timer = 0;
In the step event:
if(distance_to_object(obj_coffe_bean) > 2)
if(timer == 200) {
instance_create_layer(x, y, obj_coffe_bean, obj_coffe_bean);
timer = 0;
}
else timer++;
This works perfectly fine, coffee beans are spawned when it doesn't detect any coffee bean nearby.
The problem is that the same code doesn't work when I duplicate this object and create a spawner for another item.
The most obvious problem here would be that you are using the object index as the layer index in instance_create_layer - your code only works by chance (of there being a layer with a matching ID).
Ok, I needed to use instance_create_depth instead of instance_create_layer.

Game maker death codes which will run when you collide with a skeleton don't work like It should

p_hp is health variable and o_skeleton is our enemy. What I want to do is to kill the player when it collides with the skeleton 3 times, but it seems It doesn't work.
What did I do wrong?
p_hp=3;
if(place_meeting(x,y,o_skeleton))
{
p_hp=p_hp-1
}
if(place_meeting(x,y,o_skeleton)) && (p_hp==0)
{
instance_destroy(self);
}
Please help to solve my issue.
Is p_hp = 3 declared in the Step Event? then that means each time it reached that code, p_hp will reset back to 3. And the Step Event is called each frame.
I recommend declaring variables that'll change later on in the Create Event.
Also, It's better to use this to check if your character is still alive:
if (p_hp <= 0)
{
instance_destroy(self);
}
That way it doesn't need to collide to check if it's still alive, and if the chance happens that p_hp is lower than 0. It will still be destroyed.
Keep in mind, this possible results in the player dying instantly to the skeleton, because it's checking the Step Event each frame. In that case, you'll need to look into a short invincibility time after getting hit.
Adding invincibility after getting hit:
There are multiple ways to add invincibility, the method I use is to add invincibility would be making an invincibility variable and use that as a timer. give it a value the moment it's hit, and let it return to 0 over time. you should also add the check if the invincibility value is higher than 0;
So, in practise:
Create Event:
p_hp = 3;
invicibility = 0;
Step Event:
if (invincibility > 0) //timer in step event
{
invincibility -= 1/room_speed // (1/room_speed) = real time seconds
}
if (place_meeting(x,y,o_skeleton) && invincibility <= 0) //avoids getting hit while invincibility has value.
{
p_hp -= 1;
invincibility = 0.5; //grants half a second of invincibility after getting hit
}
if (p_hp <= 0)
{
instance_destroy(self);
}
(And as extra tip: p_hp -= 1 does the same as p_hp = p_hp - 1)

Add CCNodes to a Parent CCNode using a for loop

-(CCNode *)createFieldNode:(NSMutableArray *)fieldArray{
CGSize winSize = [CCDirector sharedDirector].viewSize;
CCNode* stackNode= [CCNode node];
for (int i; i <=fieldArray.count; i++){
//itemP is previous item in array and itemC is current item in area based on index i
BPItem*itemP;
BPItem*itemC;
if(i!=0){
itemP=[fieldArray objectAtIndex:i-1];
itemC=[fieldArray objectAtIndex:i];
float stackWidth=arc4random()%200+50;
float stackHeight=itemP.position.y+itemP.contentSize.height;
itemC.position=ccp(stackWidth,stackHeight);
}
else{
itemC=[fieldArray objectAtIndex:i];
float stackWidth=arc4random()%200+50;
itemC.position=ccp(stackWidth,0);
}
//having trouble adding multiple nodes to stackNode
[stackNode addChild:itemC];
}
return stackNode;
}
I want to add CCNodes from fieldArray on to a parent CCNode "stackNode". When I use breakpoints I am able to add the CCNode at index 0 and CCNode at index 1. However the program crash at i=2. The error I receive is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'child already added to another node. It can't be added again'
Before the crash "stackNode" has two children. I'm not adding the CCNodes one by one because I have hundreds of different arrays with many a fieldArray.count around 20. Please help I can explain more if I have been unclear.
Change the for loop start as follows :
//itemP is previous item in array and itemC is current item in area based on index i
BPItem*itemP;
BPItem*itemC; // moved out of the for loop
for (int i; i <fieldArray.count; i++){ // <- changed the end condition to avoid crash
... rest of loop
Also, in the code that creates fieldArray, make certain you have logic to ensure that there are no duplicates, otherwise you will have the same issue (but for an altogether different reason).

as3 if remove Parent Do all children get removed too?

I am loading a mc called Spiri into a mc called Box. Later I want to remove both from memory usage and off screen. I have the off screen in a tween not shown here.
If I use removeChild(box); will it also remove all Children with in?
Basically I am loading 3 movies from library with a function call. Then trying to remove them and call the same function multiple times. Which means the same movies are loaded again and again with the same names. This IS SUPPOSED TO replace the old ones but maybe its not because I am not removing them properly because by the 10th or 15th call it is getting very slow.
I am also adding an event-listener in a function too. Is that then adding a some event-Listner every single time and taking up resources as well?
It seems to be very slow after several times running a that function which makes me believe something is not getting unloaded correctly.
//I tried
box.removeChild(Spiri);
Spiri = null;
//then remove the parent like this
removeChild(box); /// but this gets an error.
again if i just do this
removeChild(Spiri); // it makes me wondering if they are getting removed.
How what is the best way to remove parent and all children in an mc?
Yes and no. The children are no longer located on the stage, but they are still children to the parent until removeChild() is called. That can be good and bad. Obviously, it is great for reusing objects but can be terrible for memory management because those objects can only be garbage collected when their parent is garbage collected. For a simple app, that is usually fine. But for something massive... not so much.
For the project I am working on now (a massive 30 page, 50,000 liner), I created a light-weight GUI framework to handle all of my DisplayObjects. Everything except basic Bitmap and Shape DisplayObjects extend a single class which extends Sprite. In that class, I have this function:
final public function destroy():void {
this.removeAllEventListeners();
var i:int, l:int, cur:DisplayObject;
l = this.numChildren;
for ( i = 0; i < l; i++ ) {
cur = this.getChildAt( i );
if ( cur is XISprite && !this.stopChildXISpriteDestroy ) {
( cur as XISprite ).destroy();
}
else if ( cur is Sprite ) {
( cur as Sprite ).removeChildren();
}
if ( cur is Bitmap && ( cur as Bitmap ).bitmapData && !this.stopBitmapDestroy ) {
( cur as Bitmap ).bitmapData.dispose();
}
if ( cur is Loader && !this.stopLoaderDestroy ) {
( cur as Loader ).unload();
}
if ( cur is Shape ) {
( cur as Shape ).graphics.clear();
}
}
cur = null;
i = l = NaN;
this.removeChildren();
}
It basically does a hard wipe of all the objects and allows me to easily qualify all children of that class for Garbage Collection. I also am keeping track of all event listeners so there is no chance at all a rogue listener could prevent GC (by calling removeAllEventListeners()). I also have some protected flags in the class that allow you to stop the destroy on a certain object type (so I can leave a SWF or an image loaded in memory if needed)
It might be overkill, but memory consumption has been an issue in this app and that function has really helped manage it. This may be more than you need, so you can just call removeChildren() with default params and it will remove all children from the parent object.
As an afterhthought: Keep your DisplayObjectContainers as simple as possible. Avoid nesting them as often as possible. The first conditional, where I call destroy on every single XISprite that is a child of an XISprite is great but it could be disastrous if there were loads and loads of XISprite children as the destroy() calls would pile up on each other and freeze the app.

Resources