• Advertisement

Advice Scalable development methodologies for small teams and one man indies

Recommended Posts

For one man indies and small teams, in unity in particular - but general answers are very welcome - what are the most worthwhile development methodologies and architectural patterns worth investing your effort into. I would like to write decent, maintainable and scalable code, but I also want to work fast. In the past I've been bogged down in process, and at the moment for my current project I'm taking inspriation from Johnathon Blow and just not overthinking or optimizing in any sense before its needed. I am certainly getting quicker output but at the same time I realize that as my projects scale I will get be more likely to get bogged down, especially with no testing or other techniques to catch regressions. So avoiding rigid bindings and singletons might be advisable. I've considered dependency injection frameworks / inversion of control containers such as zenject. The code seems ugly to me and I cant see it being a fast process. I've heard mention of essentially using the unity interface for dependency injection, but I'm not sure I comprehend.

What process would you recommend? tdd? di? testing after the fact?

what are good ways to write clean, modular, scalable code quickly with strategies to catch regressions.

Any advice is welcome and appreciated.

Share this post


Link to post
Share on other sites
Advertisement

Try out TDD, but beware that it has a larger learning curve than it initially seems to.  You will most likely do things in a cumbersome way for a while as you get used to it, and you'll most likely shoot yourself in the foot a few times with it like I have.

Try to avoid dependency injection frameworks.  They're just one more thing that can go wrong.  It's likely that you will be able to use TDD in a way that does not require dependency injection frameworks at all.  If you get to the point where you're methodically using TDD style you will be building your program bottom-up and can avoid unwanted coupling from the start.

Make things simple.  Single responsibility principle is extremely important.  Don't try to make everything an interface to appease the mythical DI gods; usually one function that does exactly what you want to do is the only one that ever needs to fulfill that interface.

For Unity, attempt to write as much of your code as possible OUTSIDE of MonoBehaviours.  MonoBehaviours have unwanted dependencies and horrid initialization restrictions compared to plain old C# classes.  It's easier to test code if it doesn't require MonoBehaviour/GameObject baggage.

Share this post


Link to post
Share on other sites

@Nypyren  Thanks for your input. I understand the value of TDD, especially in application development. The whole fact you're writing code to satisfy the contract you create with the tests should provide full covereage and result in loosely coupled classes. My main concern with test first is that I've seen quite a few competent developers recently discuss disliking tdd first, even test advocates, as they consider it to impede rapid iteration of an evolving code base. I've seen some discuss data oriented programming and some suggest you should produce tests after writing hthe code merely to catch regressions. But running a suite of unit and integration tests every build would also get annoying, I suppose it would be acceptable in unity as I mainly run in editor and then build when im ready to check some things anyway.

I would agree wth srp, It's the most vital of the solid principles. Really the only solid principle I'm not adhering to is dependency inversion, which a lot of people suggest is the most important one, at least following srp/seperation of concerns stuff. But there are other ways to achieve dependency inversion, such as service locator etc. ANy advice on that matter is also appreciated, I want to be good at my craft but also start finishing things for a change.

At least in application development I've heard that getting used to tdd makes you faster and cleaner, but I've not really found that (though my experience is limited). I also am aware of the fact that output is traditionally very slow. Mythical man month 10 lines a day stuff (though obviously skewed by working on large bases and removing lines) the talk by Johnathon Blow where he discusses  how to achieve  great enough output to be able to deliver on your indie games really change my mind on a few things, though I haven't really filled in the blanks yet.

I make extensive use of couroutines and when the choesion is high enough I would favour just using a monobehaviour, but I'll take that into consideration. I've read that af ew times lately actually, I'll do my best to avoid monobehaviour and scriptable objects when its unwanted.

At the moment I satisfy my dependencies mostly using getcomponent or passing in the inspector. Not especially convenient for designers I iamginge, given the hierarchy thus has specific structures required. I completely avoid Find searches, though they should be fast enough on projects of this scale, I don't use them for other clear reasons.

For my current project I'm using a few high level singletons. GameManager, AudioManager, etc. It's not so far been a problem but it could be done better, but I'm not sure what would be better without di.

Any more suggestions on methodologies and regression catching strategies is greatly appreciated.

ps. it annoys me that empty objects I use for maintaining structure and convenience in the hierarchy have transforms.

Share this post


Link to post
Share on other sites
8 hours ago, thatguyiam said:

I've seen quite a few competent developers recently discuss disliking tdd first, even test advocates, as they consider it to impede rapid iteration of an evolving code base.

It does both.

TDD is amazing for stuff that has a long tail.  I've used it in development for server features and code with a long-term support.

Automated tests ensure permanence. They ensure that if you modify the behavior you get test failures, which in turn prevent accidentally changing your behavior.  For libraries and for long-running code that must be supported for years, this is great.  The initial costs are offset by many years of reduced maintenance cost. 

Since it ensures permanence, that means it works against rapid iteration. For game code you intend to throw away on completion, where the code should generally be fluid and changes frequently, it is not so great.  Instead of having a collection of automated tests that remain green as you change implementation details, when you are changing visible behavior then every change requires changing a test. 

Automated tests also have a cost in time and effort.  There have been several studies on it. TDD is usually the cheapest, at about 1:1 cost for developing the main code plus developing the tests.  Adding tests immediately after writing increases the cost slightly since some things need to be modified, and adding tests to established systems tends to have significantly higher costs since parts of the system must be retooled or adapted.  So if you are going to be implementing tests, TDD is the most cost-efficient way to do it.

For your short lived code in Unity or similar engines, TDD is a poor decision from a business standpoint.  The extra investment won't be recovered over time like it would be with a long-lived project.  For a long-term system that needs to be resilient against change under maintenence, TDD is wonderful.

8 hours ago, thatguyiam said:

I make extensive use of couroutines and...


Co-routines are an easy way to introduce delayed processing without violating Unity's model where the scripts follow a reliable linear flow.  You can use the full suite of multiprocessing/multithreading code through the .net runtime, but it opens the standard sets of difficulty with parallel code. It adds about an order of magnitude of complexity to your bugs, which is a great reason for the game engine designers to direct beginner and intermediate developers away from it. 

8 hours ago, thatguyiam said:

Really the only solid principle I'm not adhering to is dependency inversion, which a lot of people suggest is the most important one

Dependency inversion is extremely important when you have many subclasses, when you have a bunch of code that gives slight variations on the implementation details but provides a common interface.

Dependency inversion usually isn't necessary if you have no subclasses, if you have only one type of thing and never need variations under a common interface.

Unity scripts already do this in many ways, such as following the GameObject interfaces, or the Component interface, Joint that is implemented by assorted hinges and springs, Renderer that applies to MeshRenderer and CanvasRenderer and BillboardRenderer and ParticleRenderer and other visual things.  For these situations you only need to know the interface, the specific type of object usually doesn't matter.

Many Unity scripts I've written over the years do not need additional interfaces.  Some advanced systems do, especially systems where you have a bunch of interchangeable parts, but when you implement them they usually don't grow from MonoBehaviour.

9 hours ago, thatguyiam said:

ps. it annoys me that empty objects I use for maintaining structure and convenience in the hierarchy have transforms.

It is a design decision, but I think a good one.  Since nearly everything else requires exactly one transform, building it directly into the object allows for space savings. Many items built for maintenance as you describe get converted into prefabs, and then you really want the GameObjects that provide structure to also have the transform.

In non-trivial worlds you constantly add and remove scenes, which means adding a GameObject container, positioning it appropriately, and loading the new scene into it.  The position is important to make the world continuous, and the container part is necessary so it can be cleanly unloaded later.

A few heavily scripted items may live inside a utility GameObject, but they're a tiny number.  Generally I've seen one single game object with a unique name and layer living at the root layer that serves as the service locator.  Find that object and use GetComponent<>() to pull off whatever service you need.

The extremely rare inconvenience is far outweighed by the large benefit to every other object in the game world.

21 hours ago, thatguyiam said:

write clean, modular, scalable code quickly

The common, standard answers apply.

Write with namespaces so one system's code doesn't interfere with another system's code, Unity does not wrap code in namespaces by default. Configure Unity's files for scenes and asset information are text based rather binary, so they can be more easily merged and integrated.  There are two models to version control, one that restricts access and locks by default, another that merges when posting and is unlocked by default; you can save headaches if you configure version control to make the files (.asset, .scene, .meta, etc.) automatically lock on checkout. Divide your systems into small enough chunks that pieces can be worked on concurrently, such as designers editing prefabs rather than editing scenes, code broken into sufficiently small subsystems, etc.

Writing clean code is basically the same regardless of the tool you're using.  It should be well named, have uniform naming standards and uniform conventions, and otherwise follow standard coding guidelines and practices.

Share this post


Link to post
Share on other sites
13 hours ago, thatguyiam said:

My main concern with test first is that I've seen quite a few competent developers recently discuss disliking tdd first, even test advocates, as they consider it to impede rapid iteration of an evolving code base. I've seen some discuss data oriented programming and some suggest you should produce tests after writing hthe code merely to catch regressions. But running a suite of unit and integration tests every build would also get annoying, I suppose it would be acceptable in unity as I mainly run in editor and then build when im ready to check some things anyway.

My experience is:

TDD slows you down because you spend time up front writing your test, then writing your code, then testing, then fixing, etc.

TDD slows you down because when you want to change how something behaves to implement a new feature, you will probably break existing tests and need to fix them.  This is where my "shoot yourself in the foot" comment stems from.  The first major set of code I attempted to use TDD style was actually a retrofit of code without any unit tests.  So my "unit" tests actually test FAR too much per test.  I change one thing and I break 50 tests.  This is bad!  This is not how TDD should work!  Tests should not overlap, so the fewest number of tests should fail per change you make, so you can either figure out if you introduced a bug, or you forgot to update the test to reflect the new behavior.

TDD DRAMATICALLY helps you get code right the first time and reduces time wasted manually testing (i.e. paying a QA team to waste their time testing stuff you can test automatically with no human salary involved).  Really good programmers can often get code right the first time without TDD, but I have had people break my code countless times so I'm learning that it doesn't matter how good I am at programming if I'm not the only person on the team.  I'm hoping that I can introduce TDD at work to prevent code from being eroded over time.

(In My Opinion) Retrofitting unit tests into code that you wrote before TDD is what causes cumbersome interfaces, which led to the development of dependency injection frameworks.  If you start with TDD you should be able to avoid writing code with interfaces that are too complex to test using simple tests and too complex to initialize easily.  I may change my mind about dependency injection frameworks later after I use TDD more but for now I don't like them because they feel like one giant code smell to me.

Edited by Nypyren

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By Ovicior
      Hey,
      So I'm currently working on a rogue-like top-down game that features melee combat. Getting basic weapon stats like power, weight, and range is not a problem. I am, however, having a problem with coming up with a flexible and dynamic system to allow me to quickly create unique effects for the weapons. I want to essentially create a sort of API that is called when appropriate and gives whatever information is necessary (For example, I could opt to use methods called OnPlayerHit() or IfPlayerBleeding() to implement behavior for each weapon). The issue is, I've never actually made a system as flexible as this.
      My current idea is to make a base abstract weapon class, and then have calls to all the methods when appropriate in there (OnPlayerHit() would be called whenever the player's health is subtracted from, for example). This would involve creating a sub-class for every weapon type and overriding each method to make sure the behavior works appropriately. This does not feel very efficient or clean at all. I was thinking of using interfaces to allow for the implementation of whatever "event" is needed (such as having an interface for OnPlayerAttack(), which would force the creation of a method that is called whenever the player attacks something).
       
      Here's a couple unique weapon ideas I have:
      Explosion sword: Create explosion in attack direction.
      Cold sword: Chance to freeze enemies when they are hit.
      Electric sword: On attack, electricity chains damage to nearby enemies.
       
      I'm basically trying to create a sort of API that'll allow me to easily inherit from a base weapon class and add additional behaviors somehow. One thing to know is that I'm on Unity, and swapping the weapon object's weapon component whenever the weapon changes is not at all a good idea. I need some way to contain all this varying data in one Unity component that can contain a Weapon field to hold all this data. Any ideas?
       
      I'm currently considering having a WeaponController class that can contain a Weapon class, which calls all the methods I use to create unique effects in the weapon (Such as OnPlayerAttack()) when appropriate.
    • By tomcalm
      So I've decided to make a multiplayer mod for dark souls, the idea was to cut a lot out of the game and make a multiplayer 5v5 or 3v3 with Stat and Item presets so the player could just go in with out the steep learning curve. 
      I have no experience what so ever. But a lot of time and patience.
    • By Vu Chi Thien
      Hi fellow game devs,
      First, I would like to apologize for the wall of text.
      As you may notice I have been digging in vehicle simulation for some times now through my clutch question posts. And thanks to the generous help of you guys, especially @CombatWombat I have finished my clutch model (Really CombatWombat you deserve much more than a post upvote, I would buy you a drink if I could ha ha). 
      Now the final piece in my vehicle physic model is the differential. For now I have an open-differential model working quite well by just outputting torque 50-50 to left and right wheel. Now I would like to implement a Limited Slip Differential. I have very limited knowledge about LSD, and what I know about LSD is through readings on racer.nl documentation, watching Youtube videos, and playing around with games like Assetto Corsa and Project Cars. So this is what I understand so far:
      - The LSD acts like an open-diff when there is no torque from engine applied to the input shaft of the diff. However, in clutch-type LSD there is still an amount of binding between the left and right wheel due to preload spring.
      - When there is torque to the input shaft (on power and off power in 2 ways LSD), in ramp LSD, the ramp will push the clutch patch together, creating binding force. The amount of binding force depends on the amount of clutch patch and ramp angle, so the diff will not completely locked up and there is still difference in wheel speed between left and right wheel, but when the locking force is enough the diff will lock.
      - There also something I'm not sure is the amount of torque ratio based on road resistance torque (rolling resistance I guess)., but since I cannot extract rolling resistance from the tire model I'm using (Unity wheelCollider), I think I would not use this approach. Instead I'm going to use the speed difference in left and right wheel, similar to torsen diff. Below is my rough model with the clutch type LSD:
      speedDiff = leftWheelSpeed - rightWheelSpeed; //torque to differential input shaft. //first treat the diff as an open diff with equal torque to both wheels inputTorque = gearBoxTorque * 0.5f; //then modify torque to each wheel based on wheel speed difference //the difference in torque depends on speed difference, throttleInput (on/off power) //amount of locking force wanted at different amount of speed difference, //and preload force //torque to left wheel leftWheelTorque = inputTorque - (speedDiff * preLoadForce + lockingForce * throttleInput); //torque to right wheel rightWheelTorque = inputTorque + (speedDiff * preLoadForce + lockingForce * throttleInput); I'm putting throttle input in because from what I've read the amount of locking also depends on the amount of throttle input (harder throttle -> higher  torque input -> stronger locking). The model is nowhere near good, so please jump in and correct me.
      Also I have a few questions:
      - In torsen/geared LSD, is it correct that the diff actually never lock but only split torque based on bias ratio, which also based on speed difference between wheels? And does the bias only happen when the speed difference reaches the ratio (say 2:1 or 3:1) and below that it will act like an open diff, which basically like an open diff with an if statement to switch state?
      - Is it correct that the amount of locking force in clutch LSD depends on amount of input torque? If so, what is the threshold of the input torque to "activate" the diff (start splitting torque)? How can I get the amount of torque bias ratio (in wheelTorque = inputTorque * biasRatio) based on the speed difference or rolling resistance at wheel?
      - Is the speed at the input shaft of the diff always equals to the average speed of 2 wheels ie (left + right) / 2?
      Please help me out with this. I haven't found any topic about this yet on gamedev, and this is my final piece of the puzzle. Thank you guys very very much.
    • By Estra
      Memory Trees is a PC game and Life+Farming simulation game. Harvest Moon and Rune Factory , the game will be quite big. I believe that this will take a long time to finish
      Looking for
      Programmer
      1 experience using Unity/C++
      2 have a portfolio of Programmer
      3 like RPG game ( Rune rune factory / zelda series / FF series )
      4 Have responsibility + Time Management
      and friendly easy working with others Programmer willing to use Skype for communication with team please E-mail me if you're interested
      Split %: Revenue share. We can discuss. Fully Funded servers and contents
      and friendly easy working with others willing to use Skype for communication with team please E-mail me if you're interested
      we can talk more detail in Estherfanworld@gmail.com Don't comment here
      Thank you so much for reading
      More about our game
      Memory Trees : forget me not

      Thank you so much for reading
      Ps.Please make sure that you have unity skill and Have responsibility + Time Management,
      because If not it will waste time not one but both of us
       

  • Advertisement