Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Best idea to manage numerous interactive elements

Discussion in 'Scripting' started by LeRan, Apr 19, 2016.

  1. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    Hello forum,

    I'm trying to make a sort of 2D-RPG game that should (hopefully) have numerous interactive elements on the map, a bit like Stardew Valley or Don't Starve. I wonder what the best idea is to manage the interaction with those elements.

    1) have a central procedure that listens to the mouse click, then cast a ray to find which element was clicked, and call corresponding scripted actions from there.

    2) attach a script to each interactive element, that listens to the click for itself with OnMouseDown or something, and that calls corresponding scripted actions.

    3) (optional) any better idea ?

    I don't know which one would be the best, in terms of game performance and code maintenance ?
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    OnMouseDown is nice in that you don't have to script any raycasting or mouse position calculation. That where it stops being fun.

    You'll want to be able to deactivate mouse input. If you're doing the OnMouseDown method, each of the implementations of that will have to check some global for "can the user press now".

    You'll want to be selective about what you can click, or what clicking something does. In Don't Starve, what clicking does depends on what tool you have equipped. You're probably looking for something similar. This, again, means that OnMouseDown needs to check some global value.

    If you're doing raycasting, your mouse script needs to find all the things the raycast hit, and send them on to the player avatar. The player avatar then handles if it should interact with those things. Finally, the things you can click handles being interacted with.

    Short version - your clickables shouldn't handle checking if they're clicked, even if Unity has support for that. Of course, if this is a prototype or a game jam or anything where speed is more important than lasting design, then go for OnMouseDown every time.
     
  3. Laperen

    Laperen

    Joined:
    Feb 1, 2016
    Posts:
    1,065
    Since its an RPG, the main interactor would still be the player's character(s), not the mouse cursor.

    With this assumption in mind, the mouse cursor delegates orders to the characters, and the character's interactions with objects are left to their own behavior. Not sure you have any option other than 1). Although if it's 2D you may not need to do raycasting, a proximity check might be sufficient.

    The main problem would be communication. Hopefully you are familiar with either C# or Unity events.
     
  4. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    Baste : thanks for this insightful feedback! I too had a certain preference for the centralized solution, but I wasn't sure why - I thought probably because I'm French, I like central planning better than individual initiative :) I think I'll do as you advise.

    Laperen : you're right, maybe mouse coordinates and a 2D Array of interactive objects with map coordinates would spare frequent computing-intensive Raycasts. On the other hand I'm not sure what you mean by "the main problem would be communication"? If you meant "knowing what clicked object is what", then I opted for said public Array, filled when the game starts (then updated and eventually saved and loaded) with map coordinates and reference to game objects. Is anything wrong with that ?
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    They're actually pretty cheap, so don't worry about a couple a frame. Just be sure that if you're doing frequent RaycastAlls, use the NonAlloc version, as otherwise garbage will build up over time.

    The communication problem is probably the three-way interaction between the mouse script, the player avatar script, and the interactable item scripts. The player-to-item interaction in particular is something you're going to be spending a lot of time creating variants of, so be extra careful with how you design that API.
     
  6. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    Hmm, maybe I'm doing something that is BAD, but I did not plan to have that problem: here is how I designed things.

    - Since I've only got one player object, I made everything player related as a global ("static") set of variables (a struct actually). The player gameObject with it's collider and sprite is instantiated, but that's all, the rest is taken care of by that global struct.

    - All data related to objects in game are also stored in a global ("static" again, I don't know why I can't become familiar with this word) array of variables. On game start or load, game objects are created that each point to their corresponding value in the array, but hardly store any parameter themselves.

    So, all interactions are pretty straightforward once I picked the reference number of the item to interact with (and I also intend to manage said interactions with a static class, a sort of "Game World Events" class). The reason I did things like this is that I found it much more convenient to save and load a gamefile that countains everything that was created and altered in the world.

    But reading posts here and there I'm starting to feel that using static structs may be qualified as evil, and only I wouldn't know that because I was shaped as programmer in the unforgiving harshness of Turbo Pascal when the world was still young. Is that so?
     
    Last edited: Apr 19, 2016
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    Globals are generally spoken of as the devil himself. That's not necessarily the case, and it's very natural to have a global player reference in a single-player game. There's a bunch of downsides, though.

    First of all, you probably want to share a bunch of scripts between your player avatar and your npcs. Things like movement scripts an health scripts. If the player's data is in a global struct, then you'll have to make that global interact with those structs in a weird way - or just re-implement movement for the player.

    You also don't want to have your game world rely to much of the state of the player avatar. That'll make it prohibitively expensive to change any aspect of the character, as every NPC script might be broken by a change in the player data. Having getting information about the player avatar globally available will make it very tempting to couple other things too tightly to it.

    Having a static procedure manage player-item interaction could be a decent idea. BUT, if you ever want to have NPC-item interaction, that'll suddenly have to be shoved into your player-item interaction script, and it'll end up awkward and ugly. Other things will also be hard to mod in.

    To sum up, global stores of data and global behaviours are bad because they're hard to change. They also tend to be big.
    Unity's core design philosophy is that you should make small, independent behaviours, and create interesting gameplay by combining that. As any design philosophy, you'll often find following it too rigorously will be detrimental, but it's probably be good to follow it at least partially.

    In particular, don't write a game in Unity like you would write a game where you have a main-loop that you're in control of.
     
    Fajlworks likes this.
  8. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    Baste: thank you very much, you made some very interesting and very useful points.
    Admitedly I didn't think of that yet since I'm coding player and environment, but have not started designing NPCs behaviour. Very good point.

    I did mention that I once had to sell my soul to Turbo Pascal to learn programming, and that it still legally owns it.