Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411273 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 01:12:53 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Best practices when planning a big game?
Pages: [1] 2
Print
Author Topic: Best practices when planning a big game?  (Read 2553 times)
Armageddon
Level 6
*



View Profile
« on: April 01, 2015, 05:27:09 PM »

Hi, I'm just curious if anyone has any suggestions or article links to help with planning the scripting of a commercial scale Unity game?
So far my development methods with Unity have been a lot of one-off scripts for individual objects with a lot of instanced and modified prefabs. It always seems to get really messy but that's what Unity is about right, if you don't have a component you need just write a quick one yourself.
I also have a lot of public GameObject type references in my scripts and then I have to hook it all up in the editor drag and drop style, but I've always been told that's not a great way and could get slow after awhile.

My games hover around 200-300 drawcalls and I really don't know how good or bad that is. I can't afford to hire a programmer and I don't know how to find a collaborator so I just want to make sure I plan the game as best as possible. Since, with being commercial, you have certain optimization standards to meet that are rather difficult as a single developer with one computer to test on.

Thanks!
Logged

teetap
Guest
« Reply #1 on: April 01, 2015, 06:55:35 PM »

Having worked with Unity for many years I currently develop my games with a single class, that is organized in several files as a C# "partial class", in order to simplify communication between methods and avoid singletons. Then I create components only when really needed, and these will only contain fields to ease entering data from the Editor. The main class then processes all the data in a usable form when the game is initialized. I use prefabs, instantiation and object pooling a lot.

Whenever something can be automated I create an Editor extension, or use automation tools that emulate user input. There are also handy tricks like naming assets in a way the script can recognize and process. The idea is getting to a point where you find that adding more content isn't constrained nor exponentially painful, but simple linear work. Large teams can and need to create complex hierarchies of classes and tools because they have the work power, but as lone developers we need to be very conscious where to invest our time, so a large project can be finished.

To know whether those drawcalls are a problem you should use the Profiler. And if you are targeting iOS, Xcode has even better tools to analyze the game that will also help other platforms.
Logged
Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #2 on: April 01, 2015, 10:00:17 PM »

So far my development methods with Unity have been a lot of one-off scripts for individual objects with a lot of instanced and modified prefabs. It always seems to get really messy but that's what Unity is about right, if you don't have a component you need just write a quick one yourself.

This is the way you're generally supposed to work with unity yea, keep in mind that you can organize things in folders and that you can create helper classes to abstract some of the work away to keep things cleaner.

I also have a lot of public GameObject type references in my scripts and then I have to hook it all up in the editor drag and drop style, but I've always been told that's not a great way and could get slow after awhile.

You can directly put in a MyComponent public field instead of a GameObject if what you're looking for is the component. This avoids the .GetComponent call, I assume this is what you mean with that it could get slow.

My games hover around 200-300 drawcalls and I really don't know how good or bad that is.

On PC this is fine, draw calls tend to hover in the thousands. On iOS, this amount I think should be just about right but profile if you wan to be sure.

Having worked with Unity for many years I currently develop my games with a single class, that is organized in several files as a C# "partial class"

This is a major violation of the single responsibility principle. It can create code re-use and maintainability problems in the long run. It would be good to look at alternatives.

Whenever something can be automated I create an Editor extension, or use automation tools that emulate user input.

This is a very good thing to keep in mind, make good use of your tools, Unity is a particularly sharp one with lots of slots for extensions.
Logged
Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #3 on: April 01, 2015, 10:31:47 PM »

Just to expand on the bit on the partial classes, the reason I advice against it is because doing that misses a big part of why not to use singletons in the first place, to avoid out of control coupling. Having one big class means all your fields are shared between the different parts, meaning everything is coupled to everything. When you make a change somewhere there's no good way to estimate what kind of effect this will have on the rest of the system. As well, it's hard to follow encapsulation when every field is accessible to every major part of your game code.
Logged
teetap
Guest
« Reply #4 on: April 02, 2015, 07:13:21 AM »

There is no violation to the single responsibility principle when using a single class, if responsibility is clearly distributed among methods and the single class is responsible for the game itself. Functions were invented to avoid code duplication in first place, so that's not going to happen when those are used appropriately. Maintainability problems in code will occur even when using multiple classes, that depends on how the code is organized.

Too much encapsulation will actually slow down coding and execution, since that requires more class hierarchy and member access design, more naming, more getter/setter implementations, and more parameter definitions; also passing data from one module to another will require copying, filtering and transforming data, in order to match the roles of the different modules and keep them maintainable, and instantiating the processing objects and their helpers. There are considerations on how this impacts languages with garbage collection.

Just try to keep encapsulation to a minimum, otherwise you will be focusing more on solving how to program, than on using programming as a tool to complete your game.
Logged
Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #5 on: April 02, 2015, 08:48:10 AM »

There is no violation to the single responsibility principle when using a single class, if responsibility is clearly distributed among methods and the single class is responsible for the game itself. Functions were invented to avoid code duplication in first place, so that's not going to happen when those are used appropriately. Maintainability problems in code will occur even when using multiple classes, that depends on how the code is organized.

Too much encapsulation will actually slow down coding and execution, since that requires more class hierarchy and member access design, more naming, more getter/setter implementations, and more parameter definitions; also passing data from one module to another will require copying, filtering and transforming data, in order to match the roles of the different modules and keep them maintainable, and instantiating the processing objects and their helpers. There are considerations on how this impacts languages with garbage collection.

Just try to keep encapsulation to a minimum, otherwise you will be focusing more on solving how to program, than on using programming as a tool to complete your game.

While I agree it slows down coding in the short term, and you may sometimes want to ignore encapsulation when doing a quick prototype, this question is on a large scale project. Encapsulation is a vital, if not the most vital, part of modern programming. The point of encapsulation is to prevent mistakes by only exposing what's absolutely needed to work with a system. As an example, it means you can't accidentally change a value behind the scenes that was only ever intended to be changed by a specific system. Not following encapsulation leaves you open to all sorts of accidental coupling that it will in anything non-trivial cause gigantic problems.

Keep in mind that a lot about programming on larger projects is about spending a little extra time now to save a lot of time later. Object oriented programming was invented for a reason. You do have alternative options in functional programming land, but that as well sets a lot of restrictions on what you can do, to make sure your code stays clean.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #6 on: April 02, 2015, 09:05:49 AM »

One giant class seems like a combination of the god class and ball of mud antipatterns.

I'd hate to inherit that for a large project. I could deal with it on a smaller project but I'd probably grumble a fair amount.
Logged

oahda
Level 10
*****



View Profile
« Reply #7 on: April 02, 2015, 09:14:41 AM »

I don't think the giant class is intended to be inherited. I understood it as, I guess, a quick hack to use functional imperative(?) programming in a strictly OOP language, kind of treating that class like the global namespace?
« Last Edit: April 02, 2015, 10:09:53 AM by Prinsessa » Logged

Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #8 on: April 02, 2015, 09:25:10 AM »

I don't think the giant class is intended to be inherited. I understood it as, I guess, a quick hack to use functional programming in a strictly OOP language, kind of treating that class like the global namespace?

Functional programming has a lot of restrictions around it to make encapsulation happen without classes. One important thing is immutability (and thus lack of any kind of global state between all the functions) which I doubt is happening here.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #9 on: April 02, 2015, 09:25:41 AM »

Yeah if you were trying to shoehorn C onto C# you could use using statements to hide the fact you have a giant static class.

I don't think the giant class is intended to be inherited. I understood it as, I guess, a quick hack to use functional programming in a strictly OOP language, kind of treating that class like the global namespace?

Functional programming has a lot of restrictions around it to make encapsulation happen without classes. One important thing is immutability (and thus lack of any kind of global state between all the functions) which I doubt is happening here.

My guess is princessa meant procedural/imperative programming.
Logged

oahda
Level 10
*****



View Profile
« Reply #10 on: April 02, 2015, 10:08:43 AM »

I might be mixing the terms up, yeah. Imperative sounds familiar. I don't do it myself.

Anyway, I'm not saying I'm in favour of this practice. Just trying to clear up what looked to me like a possible misunderstanding. Embarrassed
Logged

Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #11 on: April 02, 2015, 10:30:45 AM »

Imperative is generally what we consider a 'normal' programming language, C is procedural imperative programming. While C# is object-oriented imperative programming. (someone correct me if I'm wrong on this) But yea it sounds like the C way of doing things, which is very out of place in C#. (even then, in C I would probably myself still avoid global states in favor of just passing pointers to structs)
Logged
oahda
Level 10
*****



View Profile
« Reply #12 on: April 02, 2015, 10:45:05 AM »

Well, that's what I get for not exploring many different paradigms, I guess~

But on topic: I've had no problems organising OOP in Unity myself so far, at least. I do have one class with some public static members that I'm always going to need to access from any scene in the game throughout the entire session, and then some public methods in various classes. Rather than inheritance I just attach the same script (i.e. class) to game objects that need the same sort of functionality. Works fine for me. I also find grouping scripts into appropriate folders is a key to organisation in Unity, since a big game is inevitably going to end up with a lot of them.
Logged

Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #13 on: April 02, 2015, 10:53:27 AM »

A lot of work to organize larger projects is done for you by Unity, its APIs are carefully designed to encourage good code. A thing to keep in mind to keep your code clean is to favor object composition over inheritance. Using inheritance to give a set of classes some base functionality is a common pitfall in object oriented programming. Often a component/object composition based approach is cleaner and more flexible in the long run. This is a big reason Unity uses game objects and mono behaviors instead of of a more traditional approach like Source Engine's entities.
Logged
teetap
Guest
« Reply #14 on: April 02, 2015, 01:03:00 PM »

Using inheritance to give a set of classes some base functionality is a common pitfall in object oriented programming. Often a component/object composition based approach is cleaner and more flexible in the long run.

Often but not always. So what happens if a programmer chooses the wrong approach? The programmer will only realize it was wrong in the long run, and will have to work around a constrained system or refactor lots of code to fix the problem.

But there's actually no need to waste time deciding what OOP sugar to use every time a new type is defined, nor isolating every piece of data. What's the programmer protecting all this data from? Himself? If the code is well organized it's not easy to accidentally touch a part of the system that you don't want.

Implicit encapsulation in large projects is perfectly viable, produces cleaner code instead of cluttered calls passing every piece of information around, requires the programmer not to be lazy and organize the code, and is more optimal since there is no need to translate data between modules nor instantiate data processors.
Logged
Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #15 on: April 02, 2015, 03:10:31 PM »

Using inheritance to give a set of classes some base functionality is a common pitfall in object oriented programming. Often a component/object composition based approach is cleaner and more flexible in the long run.

Often but not always. So what happens if a programmer chooses the wrong approach? The programmer will only realize it was wrong in the long run, and will have to work around a constrained system or refactor lots of code to fix the problem.

But there's actually no need to waste time deciding what OOP sugar to use every time a new type is defined, nor isolating every piece of data. What's the programmer protecting all this data from? Himself? If the code is well organized it's not easy to accidentally touch a part of the system that you don't want.

Implicit encapsulation in large projects is perfectly viable, produces cleaner code instead of cluttered calls passing every piece of information around, requires the programmer not to be lazy and organize the code, and is more optimal since there is no need to translate data between modules nor instantiate data processors.

In the case of choosing between inheritance or object composition, usually the problem shows up weeks or months down the line. Generally this is a point where it's deemed 'too late' or 'too expensive' to change the system again. At the very least you just lost a big load of work because of it.

These techniques aren't OOP "sugar", they're ways of organizing and working with data. Yes, the programmer is in fact protecting the data from himself, many languages put big restrictions on programmers exactly for the reason that the programmer will be lazy otherwise and that causes problems down the line. (Any functional programming language's by default immutable values, Rust's unsafe blocks, C#'s unsafe blocks, the entire concept behind object oriented programming, etc)

There's also no need to "translate" data between modules if they're all within your own code-base. Surely you have one central way of representing your data and you make all your classes work with that rather than giving every class its own ways of handling that piece of data? Also, there's no such thing as "implicit encapsulation", this is just no encapsulation at all.
Logged
teetap
Guest
« Reply #16 on: April 02, 2015, 06:29:02 PM »

you just lost a big load of work because of it.

That simple lost is indeed a big issue for a lone developer, and a very possible reason to bury a large project. But don't take my words, let's compare the risks: the programmer accidentally choosing a wrong encapsulation design, VS, the programmer accidentally touching a part of the code he shouldn't. The first one is a development problem that will be difficult to detect since the coder will just deal with it and workaround until it's exponentially painful or an unavoidable barrier, in order the fix it a significative part of the project needs to be re-designed and implemented; while the second one is a logic problem that can be detected by testing (with possible automation, ie. unit testing, and in larger teams a task-force will catch them: QA), and can be fixed by debugging. Now we can see that encapsulation presents a way larger risk.

There's also no need to "translate" data between modules if they're all within your own code-base. Surely you have one central way of representing your data and you make all your classes work with that rather than giving every class its own ways of handling that piece of data?

When fully applying the single responsibility principle, each module will end up with their own way of storing and interpreting data, and that will require data translation between modules (sometimes data shape will match, but that's mere coincidence caused by a limited set of types and uses). For example, imagine one module's responsibility requires it to work with RGB color model, while another uses HSL... what happens when they share an image? To optimize that you will enforce one's responsibility on the other, which is to be considered a premature optimization by working around the principle, producing what? Low encapsulation Smiley

As a single developer no new members will be added to the team that could increase the probability of touching a 1000 volt cable, it's just me and my knowledge about the system. In long term projects I haven't trip over difficult to spot or fix problems that are caused due to minimal encapsulation. The usual problems are the common ones that have nothing to do with encapsulation: miscalculations, killing required objects, etc. But I have indeed previously suffered from choosing the wrong encapsulation. And encapsulation requires a lot of maintenance in itself. Just consider that each piece of data will need to be protected and shared in specific ways. All that decision taking and extra keyboard-typing multiplies development time, from the beginning to the end of a project. Best you can do is avoiding such situation and automating a much as possible.
« Last Edit: April 02, 2015, 07:18:39 PM by teetap » Logged
Cheezmeister
Level 3
***



View Profile
« Reply #17 on: April 04, 2015, 11:48:29 AM »

Having encapsulated myself into a corner on more than one occasion, I tend to err on the side of "bad" programming practice. The modern school of programming grooms students, primarily, for working on large business software with lifecycles longer than employee turnaround, which is not a *bad* thing, but it's not appropriate for what we do.

Of course, as your project grows, you *should* be keeping an eye out for patterns and refactoring appropriately (DRY). Just don't do it prematurely (YAGNI).

I tend to subscribe to Casey Muratori's idea of "compression-oriented programming", which I think is mostly the same as "emergent design".
Logged

෴Me෴ @chzmstr | www.luchenlabs.com ቒMadeቓ RA | Nextris | Chromathud   ᙍMakingᙌCheezus II (Devlog)
Layl
Level 3
***

professional jerkface


View Profile WWW
« Reply #18 on: April 04, 2015, 12:59:48 PM »

Of course, as your project grows, you *should* be keeping an eye out for patterns and refactoring appropriately (DRY). Just don't do it prematurely (YAGNI).

I agree with this fully, YAGNI saved me on so many occasions from over-engineering my solutions to a problem. But the important thing here is to keep a balance and not to entirely throw out the baby with the bath water.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #19 on: April 07, 2015, 11:29:36 AM »

I might be mixing the terms up, yeah. Imperative sounds familiar. I don't do it myself.

Anyway, I'm not saying I'm in favour of this practice. Just trying to clear up what looked to me like a possible misunderstanding. Embarrassed


tbh I think the term functional programming language is a misuse of the term. Functional implies functions, not immutability. A pure language I think is a better descriptor.
Logged

Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic