Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (741)
Games in Android Showcase (225)
games submitted by our members
Games in WIP (823)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Is there an OOP way of doing this?  (Read 1794 times)
0 Members and 1 Guest are viewing this topic.
Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Posted 2017-07-19 23:32:00 »

The following describes something that happens in my game:

Projectiles should apply effects to whatever character collides with them.

Here the code I have:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
for(Collision collision : rigidBody.getCollisions())
{
 if(collision.collider instanceof Character)
 {
  Character character = (Character) collision.collider;

  character.getEffects()
               .applyAll(effects);
 }
}


I am not too fond of instanceof. However, I cannot think of any satisfying way of doing this. I even googled how to replace instanceof but all the examples were rudimentary and did not apply to this situation.

Here of some other ways I thought of doing it and why I dislike them:

1. Entity type enum ---
Just a poor man's version of instanceof.

1  
2  
3  
4  
5  
6  
7  
8  
9  
for(Collision collision : rigidBody.getCollisions())
{
 if(collision.collider.getEntityType() == EntityType.CHARACTER)
 {
  Character character = (Character) collision.collider;

  character.getEffects().applyAll(effects);
 }
}


2. Keep a list of all characters ---
This also does not scale well because I could keep doing this and have 10-20 (maybe exaggerated) lists for each subclass. Also, my scene, which would store these list, does not care what the subclass is. However, the fields of my scene do need to know, making this complicated.

1  
2  
3  
4  
5  
6  
7  
8  
9  
for(Collision collision : rigidBody.getCollisions())
{
 if(scene.getCharacters().contains(collision.collider)
 {
  Character character = (Character) collision.collider;

  character.getEffects().applyAll(effects);
 }
}


3. Keep a list of all characters #2---
This is just a reverse way of the previous example that does not require casting.

1  
2  
3  
4  
5  
for(Character character : scene.getCharacters())
{
 if(rigidBody.getCollisions().contains(character))
  character.getEffects().applyAll(effects);
}


Offline KevinWorkman

« JGO Plugged Duke »


Medals: 270
Projects: 12
Exp: 12 years


HappyCoding.io - Coding Tutorials!


« Reply #1 - Posted 2017-07-20 01:51:02 »

How exactly is
1  
rigidBody.getCollisions()
calculated? What exactly does it contain?

If you need to know which characters are colliding with
1  
rigidBody
, why don't you have something like this:

1  
2  
3  
4  
for(CharacterCollision collision : rigidBody.getCharacterCollisions())
{
  collision.getCharacter().getEffects().applyAll(effects);
}


I'm also a little suspicious of why you need to keep around a collection of what's colliding instead of doing applying the effects whenever you detect the collision, but in any case I think you should just go with whatever makes the most sense to you.

HappyCoding.io - Coding Tutorials!
Happy Coding forum - Come say hello!
Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Reply #2 - Posted 2017-07-20 02:56:27 »

@Kevin

I store collisions because I want to handle all logic in my update method. 'Tis also the reason why I don't use observers. Both, observers and polling, work in this scenario. Observers, however, tend to be slightly harder to follow because they have no linear order.

'Tis not much different from my #2 and #3. Similarly, I want to minimize coupling of my character logic and physics logic. TBH, I would rather couple my scene and characters than my physics and characters because neither rigid body nor its fields need to know about characters.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 65K
« Reply #3 - Posted 2017-07-20 16:01:33 »

A modified #2 looks like a viable and - very good - simple solution.
Except using enhanced loops maybe.

2. Keep a list of all characters ---

1  
2  
3  
4  
5  
6  
for (Collision collision : rigidBody.getCollisions()) {
   Character character = scene.getCharacters().get(collision.collider)
   if (character != null) {
      character.getEffects().applyAll(effects);
   }
}


Lethal Running - a RPG about a deadly game show held in a futuristic dysoptian society.
Offline SteveSmith
« Reply #4 - Posted 2017-07-20 18:30:07 »

You don't say where "effects" comes from.  However, I would use an interface like ICollideable:-

1  
2  
3  
interface ICollideable {
    void applyAll(Effects[] effects); // Maybe give it a more appropriate name and signature depending on how it's used.
}


then use it such as:-

1  
2  
3  
4  
5  
6  
7  
8  
for(Collision collision : rigidBody.getCollisions())
{
 if(collision.collider instanceof ICollideable)
 {
   ICollideable collideable = (ICollideable) collision.collider;
   collideable.applyAll(effects);
 }
}


The, for every entity that should do something when collided with (i.e Character), implement the interface.

Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Reply #5 - Posted 2017-07-20 20:31:58 »

@SteveSmith
It should not matter where effects come from. But if you are curious, Effects is just a class that handles how my characters receive effects. Normally, I would keep this logic in my Character class but the class was getting long and so I encapsulated some logic in a field. A good a metaphor for this is a flash drive. It works like it was always there, a part a memory, but it is an addition to the system. I'm sure there is a design pattern for this.

Also, your solution is just a not so much different way of my initial method.  


Anyway, here is what I decided to do
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
private void handleCharacterCollisions()
{
   for(Character character : scene.getCharacters())
   {
      if(collisionWith(character))
         character.getEffects().applyAll(effects);
   }
}
   
private boolean collisionWith(Character character)
{
   for(Collision collision : rigidBody.getCollisions())
   {
      if(collision.collided == character.getRigidBody())
         return true;
   }
     
   return false;
}


After some research, I found that an, possibly the only, OOP/casting-less way to do this is to use some form of the Visitor pattern. You simply find some way of handle all relevant subtypes instead of the abstracted supertypes.

I also believe that core problem stems from my code needing to search elsewhere for data. Ideal code, however, has all necessary data in the fields of the class or in the arguments of the method and so casting is never necessary. I am also starting to believe that I am using inheritance wrong. I do not think code in ANY project should store objects as their supertype and then rely on the specialized methods or data of the subtypes.
Offline 65K
« Reply #6 - Posted 2017-07-20 20:52:50 »

Lots of looping now when always checking each character instead of only processing actual collisions.

Lethal Running - a RPG about a deadly game show held in a futuristic dysoptian society.
Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Reply #7 - Posted 2017-07-20 22:01:17 »

Lots of looping now when always checking each character instead of only processing actual collisions.

Hardly. I cull my collisions. The average is def less than 1. Similarly, there will not be that many characters. The most is prob around 50. Average is prob 10, maybe 20. This particular code only runs for projectiles too and so its running even less.

Also, i can optimize later.
Offline 65K
« Reply #8 - Posted 2017-07-21 05:20:35 »

Ok, let's say it like this: in opposite to your first solutions you now have one that is neither simple nor does it solve the original underlying design problems but adds a new (having to loop all characters to match a collision instead of a direct access).

Lethal Running - a RPG about a deadly game show held in a futuristic dysoptian society.
Offline SteveSmith
« Reply #9 - Posted 2017-07-21 07:07:43 »

@SteveSmith
Also, your solution is just a not so much different way of my initial method.  
...
You simply find some way of handle all relevant subtypes instead of the abstracted supertypes.

By using an interface, you can get any class (subtype or supertype) to handle the collisions by implementing the interface, and you will only need one "if (instanceof...)" line.  (I'm assuming that other classes, not just Character, will need handle collisions).

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Reply #10 - Posted 2017-07-21 12:07:24 »

Ok, let's say it like this: in opposite to your first solutions you now have one that is neither simple nor does it solve the original underlying design problems but adds a new (having to loop all characters to match a collision instead of a direct access).

I think it is pretty simple. Its almost exactly the same as the description. Also, I said that I don't want to couple physics with character logic.
Offline tariqbroadnax

Junior Devvie


Medals: 2
Exp: 3 years



« Reply #11 - Posted 2017-07-21 12:12:01 »

By using an interface, you can get any class (subtype or supertype) to handle the collisions by implementing the interface, and you will only need one "if (instanceof...)" line.  (I'm assuming that other classes, not just Character, will need handle collisions).

Ok but what if I want to do something else after a collision. Do I create another interface? No. Observers and polling make much more sense.
Offline dime26

JGO Ninja


Medals: 60
Projects: 7
Exp: 12 years


Should traffic wardens be armed?


« Reply #12 - Posted 2017-07-21 14:05:59 »

Might be worth looking at how Box2D handles collisions: http://www.iforce2d.net/b2dtut/collision-callbacks
Offline KevinWorkman

« JGO Plugged Duke »


Medals: 270
Projects: 12
Exp: 12 years


HappyCoding.io - Coding Tutorials!


« Reply #13 - Posted 2017-07-22 01:39:43 »

It seems like you didn't really want any advice, as you've shot down every idea that's been presented pretty abruptly. It's completely fine to do things your own way (in fact I think you should do whatever makes the most sense to you), but if what you really want to do is explain your approach to us instead of take suggestions, then maybe next time just make a post outlining your approach instead of asking for input. Tongue

Judging from your other replies, you probably don't want to hear any feedback, but I'll just quickly note that you shouldn't get too hung up on "the OOP way" or "design patterns" before you really understand what they mean. Write code that works and makes sense to you.

Anyway, it sounds like you're having fun working on your code, and that's what matters. Good luck, and happy coding.

HappyCoding.io - Coding Tutorials!
Happy Coding forum - Come say hello!
Offline abcdef
« Reply #14 - Posted 2017-07-22 04:46:07 »

I understand the problem as I have encountered it before too.

I would either add a default method called "processCollision" if Collision is an interface or a normal method if it is an abstract class / class and leave it by default empty. Ie by default nothing is done. I would then override this method for Character only and put your effect code logic there.

I have to agree with Kevin a bit that it is a little bit confusing as to what constraints you are trying to put on yourself in the design. If you don't want any code to be in character at all then you are making things complicated on yourself because there is no pretty way to filter for specific sub classes. Overloading won't work because you can't overload on a super class and a sub class as your sub class satisfies both method signatures. At this stage you have run out of OOP ways of doing things and you are left with the ugly solutions you have suggested.
Pages: [1]
  ignore  |  Print  
 
 

 
xxMrPHDxx (13 views)
2017-11-21 16:21:00

xxMrPHDxx (10 views)
2017-11-21 16:14:31

xxMrPHDxx (10 views)
2017-11-21 16:10:57

Ecumene (113 views)
2017-09-30 02:57:34

theagentd (148 views)
2017-09-26 18:23:31

cybrmynd (246 views)
2017-08-02 12:28:51

cybrmynd (242 views)
2017-08-02 12:19:43

cybrmynd (242 views)
2017-08-02 12:18:09

Sralse (257 views)
2017-07-25 17:13:48

Archive (875 views)
2017-04-27 17:45:51
List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05

SF/X Libraries
by SkyAphid
2017-03-02 06:38:56

SF/X Libraries
by SkyAphid
2017-03-02 06:38:32

SF/X Libraries
by SkyAphid
2017-03-02 06:38:05

SF/X Libraries
by SkyAphid
2017-03-02 06:37:51
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!