What's best (Custructor Arguments)

Started by
10 comments, last by Orymus3 9 years, 6 months ago

Hi,

I've been trying to minimize the amount of arguments I'm passing to my constructors to keep them under 5 (preferable 1 or 2 in most situations) because I've been told this is generally better (and I tend to agree).

My question is, what's generally best when faced with a scenario such as this one:

I create a bullet instance. I'll need to know a lot about the 'weaponComponentObject' that created this bullet (such as damage, damage type, velocity, range, etc.)

Would it be better to:

A - Pass the actual weaponComponentObject as an argument to the constructor of my bullet and stash references to all of the variables I need afterwards

or

B - Pass only the values I need (damage, damage type, velocity) to an exhaustive list in the constructor so that the bullet knows as little as possible and isn't 'heavy' on RAM

I keep finding scenarios where I'm not sure whether passing Objects is going to be very costly on RAM and can't come to a definite conclusion on this...

(Also, feel free to jump in if you feel both A and B are totally wrong and shouldn't even be mentioned on this earth)

Advertisement

You can mix both into C - move those variables into a BulletDescriptor, and make the WeaponComponent and the bullet have references to a descriptor.

Or even copies of the descriptor. People tend to totally over-estimate the cost of copies and under-estimate the cost of references. There are plenty of cases where copying a small struct is cheaper than maintaining a reference to one, especially if you consider cases where the cache is cold.

Sean Middleditch – Game Systems Engineer – Join my team!

EDIT: Oh wait, you meant an actual File descriptor correct?

That makes a lot of sense, especially since I believe these will be static values.

Never implemented file descriptors before though...

From your options I'd say B looks better, but not for the RAM involved. I don't know how many bullets you expect to have, but a few parameters on the constructor might not make a difference at the end, so always think first about the design, and later test for performance bottleneck. If the design is good you should be able to change the code for best performance easily. I chose B because it looks weird to have a reference for the weapon in the bullet, if I think about a bullet as a game object I don't care where it came from, the bullet itseld (type, speed) should be enough.

I like the suggestion of Hodgman, though, a BulletDescriptor object can hold the information you want, and makes sense in both Weapon and Bullet objects. And as a note, an abstract class doesn't mean "no functions or constructor", it means you can not instantiate an object of that class directly, you need a derived non-abstract class. The classic example is: For geometric shapes it makes no sense to have a "Shape" instance, but it makes sense to have a "Rectangle" instance that is also a "Shape", so "Shape" is an abstract class.

Anyway, you can also set the values for the bullet after you created it instead of passing everything as parameters on the constructor. This way you can have some default values and only "pass" the values that are not default.


if I think about a bullet as a game object I don't care where it came from, the bullet itseld (type, speed) should be enough.

You may need to know who the controller was (to avoid friendly fire?), the asset you need to produce (depending if there are different types of bullets), the sound you'd like to play (although, reasonably, you're likely to set that up in an audioController to prevent spreading audio havoc!). In addition, a lot of sub-parameters can be easily accessed if you know the weapon that fired it:

Damage type, velocity, spread (or deviation), etc.

I'm assuming complex bullets here, obviously, but given the sheer amount of information I need the bullets to know about, it feels like a "cleaner" solution to just pass the weaponComponent along...

One thing I'm worried about is if, for any reason, my weaponComponent needs to keep a reference to all bullets it has ever fired, would that could an infinite reference?

:: The bullet has a reference to the weaponComponent, which has a stashed ref of all bullets it has fire, hence, that bullet's reference to its weaponComponent contains the stashed lists which inherently contains itself, and so on and so forth... Perhaps I misunderstand how "references" work though.


if I think about a bullet as a game object I don't care where it came from, the bullet itseld (type, speed) should be enough.

You may need to know who the controller was (to avoid friendly fire?), the asset you need to produce (depending if there are different types of bullets), the sound you'd like to play (although, reasonably, you're likely to set that up in an audioController to prevent spreading audio havoc!). In addition, a lot of sub-parameters can be easily accessed if you know the weapon that fired it:

Damage type, velocity, spread (or deviation), etc.

The friendly fire is a good concern, I didn't think about it, but then again, I'm not sure how a weapon reference will help you with that, you actually need a player reference to know who fired it, unless each weapon can only be used by a group of players of the same team.

The sound as part of the bullet looks weird, I'd play the sound as part of the the weapon, not as part of the the bullet. I don't know much about weapons and bullets, so maybe the weapon and the bullet combination does change the sound, but most of the times a firing sound for each weapon is enough.

The asset could be infered by a "type" of bullet instead of the weapon itself.

If you'll create complex bullets maybe you can use the factory design pattern, it let's you encapsulate the creation of objects in a more flexible way.

About the last paragraph, you need a reference to each bullet object? Why do you need all that information? The bullets will always be there in the game even after hitting something or leaving the play field? Can't you just have some counters and stats instead?

About that infinite reference you mentioned, you don't have the references repeated, what you mentioned was a navigation through the same objects, but the references are always the same, you don't need more ram or resources when something like that happens. But, even if it's not a problem, sometimes it's a sign of overcomplicating the design, making things know each other when you don't really need that. I still think that, if I was working with weapons and bullets, the bullets would not know about the weapons. I've made a simple shoot em up this way some months ago and it worked, even avoided the friendly fire setting a flag on the bullet to know if I shooted it.

Anyway, you mentioned the reference as a parameter on the constructor, but you are not forced to keep the reference to the weapon forever. You can use it to initialize other fields and forget about it. Think about it, if you fired a bullet, the bullet only needs the damage of the weapon at the time it was fired. This way, if the weapon damage increases while the bullet is in the air, the bullet is not affected.


The friendly fire is a good concern, I didn't think about it, but then again, I'm not sure how a weapon reference will help you with that, you actually need a player reference to know who fired it, unless each weapon can only be used by a group of players of the same team.

In my previous project, I would create a finite number of weaponComponents as children to my ship. Each of them would swap content based on what weapon was equipped. I agree that passing the "owner" of the weaponComponent to the bullet through that particular line of logic was very crappy. I think I stashed a controller reference that basically went something along the lines of weaponComponent.getShip.getController (my eyes are bleeding)


The sound as part of the bullet looks weird, I'd play the sound as part of the the weapon, not as part of the the bullet. I don't know much about weapons and bullets, so maybe the weapon and the bullet combination does change the sound, but most of the times a firing sound for each weapon is enough.

Actually, it would need to occur at a higher level. If it was a "per weapon" trigger, I'd end up with sounds overlapping unnecessarily (and sounding louder) when more than one weaponComponent is triggered by the same input. I ended up firing a message and catching it in the audioController instead to avoid duplicates.


If you'll create complex bullets maybe you can use the factory design pattern, it let's you encapsulate the creation of objects in a more flexible way.

I've always been hesitant to jump into Factories because I met a Tech Director that told me they were "crap". Now, obviously, I'm always careful when I am not given any reason, but given I generally respect his opinions as far as tech stuff goes, I figured there might be a reason to avoid these?


About the last paragraph, you need a reference to each bullet object? Why do you need all that information? The bullets will always be there in the game even after hitting something or leaving the play field? Can't you just have some counters and stats instead?

I don't believe I'll need this, but I did in my previous prototype because I needed to find a way to cleanly dispose of dead bullets in HTML5. Now, obviously, Unity is far more permissive and I can just call a destroy on self after calling for an explosion and this allows me to cleanly dispose of bullets that have outlived their own use.


About that infinite reference you mentioned, you don't have the references repeated, what you mentioned was a navigation through the same objects, but the references are always the same, you don't need more ram or resources when something like that happens. But, even if it's not a problem, sometimes it's a sign of overcomplicating the design, making things know each other when you don't really need that. I still think that, if I was working with weapons and bullets, the bullets would not know about the weapons. I've made a simple shoot em up this way some months ago and it worked, even avoided the friendly fire setting a flag on the bullet to know if I shooted it.

Thanks, that helps, and you're probably right!


Anyway, you mentioned the reference as a parameter on the constructor, but you are not forced to keep the reference to the weapon forever. You can use it to initialize other fields and forget about it. Think about it, if you fired a bullet, the bullet only needs the damage of the weapon at the time it was fired. This way, if the weapon damage increases while the bullet is in the air, the bullet is not affected.

Yes, I believe that is where I was headed with solution A: pass it as an argument to the constructor, but strip it down in the constructor to stash references to values I actually intend on using, and not actually stash a reference to the actual weaponComponent locally. This allowed me to limit the amount of arguments I'd pass to the constructor and felt like it would result in more readable code:

A: Argument: weaponComponent

Stripped down into various variables during constructor

only these vars ever get used by the bullet

B: Argument: all vars I intend on using from the original weaponComponent

Constructor bridges all arguments to local vars

A felt cleaner, but you seem to be hinting at B?

If your constructor takes too many arguments, well... make it two classes or more. biggrin.png

Could be a sign that your class tries to do too much.

I've always been hesitant to jump into Factories because I met a Tech Director that told me they were "crap". Now, obviously, I'm always careful when I am not given any reason, but given I generally respect his opinions as far as tech stuff goes, I figured there might be a reason to avoid these?

No disrespect, but that advice itself is crap. Factory classes have their place, and are only crap when used poorly.

One reason to use a factory to construct bullets is to eliminate unnecessary dependencies. For such complex bullets as you are discussing, it's probable that you would need to pass arguments to the constructor that the calling code otherwise wouldn't need at all, and shouldn't really know about or care about. Pass that responsibility off to a factory and watch your code become simpler, more loosely coupled, and less spaghetti like.

Googling "when to use the factory design pattern" can prove informative. ;)

This topic is closed to new replies.

Advertisement