Skip to main content
GameDev.net gamedev.net
🔒 Locked

Dynamic monster object generation help

Started by blesseddisciple Jan 4, 2019 at 1:45 AM 16 replies 4.2k views
Original Post
blesseddisciple
blesseddisciple

Hello everyone, I need some help with my Java text-based dungeon crawler. My issue is that i want to spawn a monster with random specs every time a player enters a new room, pending an encounter roll. I am trying to figure out the best way to do this, logic wise. Should I (can I) just recreate the same object each time like, Monster monster=new Monster(); and have the class constructor use random number modifiers to construct the object each time. Is there a way to destroy the object so I can remake it with the same variable name?


Any help is appreciated here. Thanks

Septopus
Septopus

Just re-use the same variable then you have less garbage in your memory. ;) Unless I'm missing something, I'm not a Java programmer, but I'm pretty sure that would be just fine. If you only ever have 1 monster active, then just declare the variable with enough scope to be seen by all the code needing to interact with it. Then each time you monster = new Monster, you'll be re-using the same bit of memory and saving yourself the allocation time and garbage collection. If you have multiple monsters, then use an array or something similar instead.

And then yeah, just pass in the new random specs into the constructor and you'll have a new different monster. ;)

blesseddisciple
blesseddisciple
16 minutes ago, Septopus said:

Just re-use the same variable then you have less garbage in your memory. ;) Unless I'm missing something, I'm not a Java programmer, but I'm pretty sure that would be just fine. If you only ever have 1 monster active, then just declare the variable with enough scope to be seen by all the code needing to interact with it. Then each time you monster = new Monster, you'll be re-using the same bit of memory and saving yourself the allocation time and garbage collection. If you have multiple monsters, then use an array or something similar instead.

And then yeah, just pass in the new random specs into the constructor and you'll have a new different monster. ;)

Sweet, I was hoping that might work but i was worried about the first class object being stuck in memory after reassigning the variable to a new class object. But if the previous "new Monster()"s will get cleaned up since they are no longer bound to a variable then it should work fine. I'm not the greatest at memory managment :/

Septopus
Septopus

I'm fairly certain it will work just fine. Once you have declared the variable, it grabs all the memory it could ever need, then when you take that same variable and RE-new Monster() it just reuses the old memory instead of finding a new free location and then cleaning up the old one.

If however you are using a local Monster variable within a class that itself gets recreated frequently then it will still be fine, but it will use more memory and create more garbage for the collector. ;)

Zakwayda
Zakwayda

I'll offer some of my own thoughts. They may differ a little from what's already been said, but I'll leave it to the OP to research and figure things out as needed :)

First, I think it's worth considering whether garbage is even a problem. To the best of my knowledge there are two potential issues at play with respect to garbage here: fragmentation, and garbage collector pauses.

With respect to fragmentation, I believe most Java GCs are compacting, in which case it shouldn't be a problem. Possible exceptions would be mobile platforms like Android, but if I remember correctly, more recent versions of Android include a compacting collector (readers should of course feel free to correct me if I'm wrong about that). In any case, if you can determine that the implementations you're targeting have compacting collectors, fragmentation shouldn't be a concern, I don't think.

Regarding GC pauses, I've read that Java GC implementations have gotten better over time about this, and that (perhaps) techniques like pooling (that is, reuse of existing objects) are no longer thought to be necessary or best practice, generally speaking. Again, this might differ on mobile platforms. But if your game is text-based and not real time, it seems unlikely that pauses would be an issue.

So first, I'd consider whether garbage is even something you need to worry about.

14 hours ago, Septopus said:

Then each time you monster = new Monster, you'll be re-using the same bit of memory and saving yourself the allocation time and garbage collection.

14 hours ago, Septopus said:

Once you have declared the variable, it grabs all the memory it could ever need, then when you take that same variable and RE-new Monster() it just reuses the old memory instead of finding a new free location and then cleaning up the old one.

Although I may be misinterpreting, I'm not sure if this is exactly right as stated. Similar to e.g. C++, I don't think repeatedly new'ing new objects, even if assigned to the same variable, is guaranteed to reuse the same memory.

A bit of conceptual evidence for this is that even if you 'new' a new object and assign it to a variable, the VM isn't necessarily done with the previous object yet (it still needs to be collected, finalized, etc.). So it seems unlikely that the same memory would or could be used for the new object.

14 hours ago, blesseddisciple said:

Sweet, I was hoping that might work but i was worried about the first class object being stuck in memory after reassigning the variable to a new class object. But if the previous "new Monster()"s will get cleaned up since they are no longer bound to a variable then it should work fine.

They won't get stuck (under normal circumstances at least). As you suggest, objects will be collected (eventually) once they're no longer reachable.

Again though, I wouldn't necessarily worry about garbage at all unless you have good reason to think that fragmentation and/or GC pauses are likely to be an issue. Further, I'd suggest that working around the garbage collector (e.g. by pooling or whatever) can sometimes lead to poorer quality code. This post is long enough as is, so I'm not going to elaborate on that here, but if I'm right about that, it might be a reason not to worry about garbage unless you have good reason to.

Septopus
Septopus
14 minutes ago, Zakwayda said:

it might be a reason not to worry about garbage unless you have good reason to.

Premature optimization I believe it's called. Like I said, I'm not a Java programmer I suppose it has more to do with the Java VM inner workings then.. It is best practice to avoid garbage collection in most other languages that I'm familiar with that have the facility(certainly helps in c#). ;) I may have to expand my understanding of the topic.

blesseddisciple
blesseddisciple

Yeah, i guess i just wish there was a keyword like monster=deallocate(); or something so that I knew for sure it was emptied before reassignment.

But as slow as a,text based game runs, shouldn't be an issue I guess.

Septopus
Septopus
24 minutes ago, blesseddisciple said:

Yeah, i guess i just wish there was a keyword like monster=deallocate(); or something so that I knew for sure it was emptied before reassign

Unless you are actually generating an error related to this, then I would put the possibility fairly far out of mind. Java and other languages with garbage collection are built to handle this side of memory management for you. When a variable goes out of scope it will get cleaned up at some point before that memory is reused. ;)

Zakwayda
Zakwayda
1 hour ago, blesseddisciple said:

Yeah, i guess i just wish there was a keyword like monster=deallocate(); or something so that I knew for sure it was emptied before reassignment.

Just to offer further reassurance, you don't have to worry about this.

Here's a way you can possibly convince yourself of this. It's unlikely that a language as widely used as Java, and with as long a history as Java has, would be missing a critical memory management feature. As such, I think you can consider the absence of something like a 'delete' or 'deallocate' keyword as good evidence that such a feature wouldn't offer anything beyond what's already available, and/or would be inconsistent with the language design.

If you're not already familiar with how these sorts of garbage collectors tend to work, you might find further reassurance by investigating that a bit. A simple way of describing it is that objects are released to the garbage collector once you no longer hold any references to them, after which they're guaranteed to be 'deleted' eventually. Some consideration should show that such a system is functionally and semantically complete by itself and that an explicit delete/deallocate operator wouldn't add anything.

There are additional details of potential interest, such as the now-deprecated 'finalize' function; reference graphs, reachability, 'islands of isolation', and what exactly qualifies an object for collection; weak and soft references; and so on. But, these are all academic and/or (I would guess) unlikely to be relevant for what you're doing currently.

Alberth
Alberth
On 1/4/2019 at 3:49 AM, Septopus said:

If you only ever have 1 monster active, then just declare the variable with enough scope to be seen by all the code needing to interact with it. Then each time you monster = new Monster, you'll be re-using the same bit of memory and saving yourself the allocation time and garbage collection.

This is completely wrong. In Java, only the native types (boolean, int, and float like types, written all lower case) have enough internal storage to store their value. Everything else, including boxed types like Integer are objects. Object variables do not contain object data, they are references to heap-allocated memory. In C++ you'd call them pointers. Every 'new' creates a new object of the given type.

In other words, each "new Monster();" makes a new fresh object.


12 hours ago, blesseddisciple said:

Yeah, i guess i just wish there was a keyword like monster=deallocate(); or something so that I knew for sure it was emptied before reassignment.

Java has automatic garbage collection. it removes objects that cannot be longer accessed. You can access an object if there exists a variable that references the object. after "monster = new Monster();" the "monster" variable references the Monster object, so it will not be removed. Variables also exist in objects, This system thus extends to object variables in objects as well. If you have a "Room room" variable in your Monster (the room it currently is), then that room will not be removed, since the Monster object refers to it, and the Monster object is not removable too (since "monster" refers to it.)

So what you want is somewhat already possible. If you write "monster = new Monster()", the 'monster' variable now refers to the new Monster object. It does no refer to the previous Monster oibject any more. If no other variable in your program refers to the old Monster object with that assignment (ie "monster" was the last variable refering to the old Monster), the old Monster object will be removed from the program.

You cannot forcibly de-allocate an object in Java. Imagine what would happen:


// 1 object Monster, and 2 variables both refering to the same object.
Monster monster = new Monster();
Monster monster2 = monster;

deallocate monster; // Say we could de-allocate
// The Monster object is now gone, what would monster2 refer to now?
// Garbage?

Note that you can also remove the Monster object reference from the monster variable by a simple "monster = null;" assignment. "null" points to no object at all, and thus also not to the Monster object. This is not often needed though. Normally, you either assign a new object, or you drop the variable from the scope (ie you cannot access the variable itself any more), for example by leaving the function that had the (local) monster variable in it.


I agree with others that garbage collection is a non-issue in a text-based game, so just don't worry about it at all.

If you want to re-use the Monster object, you "new" it once at the start of the program, and then for each next monster, you re-initialize the object, like "monster,restart();". Note that in this way the game always has a monster object, so if the player can kill it, you need some sort of inactive or 'dead' state for the monster for the time between the kill and the respawn.

blesseddisciple
blesseddisciple
8 hours ago, Alberth said:

Note that in this way the game always has a monster object, so if the player can kill it, you need some sort of inactive or 'dead' state for the monster for the time between the kill and the respawn.

In my monster class I have a boolean 'alive'. Basically the only time 'fighting' is activated is when a character moves to a new room and the room contains a monster. A new monster is rolled and alive is set to true. The fighting continues until alive=false. Then fighting is set to false. Then normal room actions continue. I also have things like monster.'level', a boolean for 'magic' type, and other stats. I reference these in my main method. So if I replace the old monster object with a new one, that means the monster.alive (etc.) will stop pointing at the old object entirely so it can be destroyed by GC, correct?

Zakwayda
Zakwayda
45 minutes ago, blesseddisciple said:

So if I replace the old monster object with a new one, that means the monster.alive (etc.) will stop pointing at the old object entirely so it can be destroyed by GC, correct? 

Primitive fields like your 'alive' boolean don't point at anything, but I'm guessing what you're asking about is the relationship (if any) between different instances of a class. If that is in fact the question, then yes, generally speaking each instance is separate and discrete from and unrelated to all other instances (aside from static fields).

Again, what keeps objects alive is (strong) references to them. A boolean field (to use your example) isn't a reference, so it plays no role in object lifetime.

Does that answer your question? Or is it something else that you're asking about?

VoxycDev
VoxycDev

I would have an array of computer objects named "computer1" to "computer32" and reuse them for whatever monsters are necessary by changing their properties. If hitpts are over 0, the object is alive. Ones that are not needed currently have visible set to false. The array is re-created for every different level (with needed length), but stays consistent during a particular level. I find that not re-creating or deleting objects during game play helps with debugging and helps avoid memory leaks in C++. In Java, maybe there would also be a small benefit of GC not being invoked unnecessarily (less jitter).

Zakwayda
Zakwayda
23 minutes ago, VoxycDev said:

I would have an array of computer objects named "computer1" to "computer32" and reuse them for whatever monsters are necessary by changing their properties. If hitpts are over 0, the object is alive. Ones that are not needed currently have visible set to false. The array is re-created for every different level (with needed length), but stays consistent during a particular level. I find that not re-creating or deleting objects during game play helps with debugging and helps avoid memory leaks in C++. In Java, maybe there would also be a small benefit of GC not being invoked unnecessarily (less jitter).

Some of these issues have already been discussed in the thread, but I think some reiteration might be appropriate here.

The impression I have is that the OP is working on a non-realtime game, in which case jitter (which is, I would say, generally a realtime phenomenon) shouldn't be an issue. Even in a realtime context I think it'd be worth considering whether pooling would actually be beneficial (see earlier in the thread for some relevant discussion).

Since the OP isn't using C++, I'll leave that part aside. I think it's worth considering though that pooling can introduce code quality issues that wouldn't exist otherwise, e.g. with respect to mutability, lifetime management, and so on. Pooling may be preferable and even necessary in some cases, but I'm not sure it should be preferred by default, or that it would be particularly appropriate for what the OP appears to be doing.

blesseddisciple
blesseddisciple
10 hours ago, Zakwayda said:

Primitive fields like your 'alive' boolean don't point at anything, but I'm guessing what you're asking about is the relationship (if any) between different instances of a class. If that is in fact the question, then yes, generally speaking each instance is separate and discrete from and unrelated to all other instances (aside from static fields).

Again, what keeps objects alive is (strong) references to them. A boolean field (to use your example) isn't a reference, so it plays no role in object lifetime.

Does that answer your question? Or is it something else that you're asking about?

What I meant was that my monster class has an instance variable like so, monster.alive.. So, if I reference monster.alive in my code it will be looking at whatever object is currently set to monster variable. Then, once the new object is assigned to monster, monster.alive looks at the new object and the old reference is gone correct. Just verifying I understand correctly.

Alberth
Alberth

Yes it is. You can easily test such things yourself by doing a small experiment, for example:


public class Monster {
    public final int number;
    public boolean alive;

    public Monster(int number, boolean alive) {
      this.number = number;
      this.alive = alive;
    }

    public static void main(String[] args) {
      Monster m = new Monster(1, true);
      Monster p = m;
      System.out.printf("m: Monster %d, alive=%s\n", m.number, m.alive);
      System.out.printf("p: Monster %d, alive=%s\n", p.number, p.alive);

      System.out.printf("\nKill monster m\n");
      m.alive = false;
      System.out.printf("m: Monster %d, alive=%s\n", m.number, m.alive);
      System.out.printf("p: Monster %d, alive=%s\n", p.number, p.alive);

      System.out.printf("\nNew monster m\n");
      m = new Monster(2, true);
      System.out.printf("m: Monster %d, alive=%s\n", m.number, m.alive);
      System.out.printf("p: Monster %d, alive=%s\n", p.number, p.alive);
    }
}

Output:


m: Monster 1, alive=true
p: Monster 1, alive=true

Kill monster m
m: Monster 1, alive=false
p: Monster 1, alive=false

New monster m
m: Monster 2, alive=true
p: Monster 1, alive=false

With experiments like these it's simple to try anything you want to know. There is no substitute for experiencing it yourself!!

Note that my "System.out.printf" calls are a bit weird perhaps. I grew up with C, so I am used to this function. You may want to find another print function that suits you better.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.