|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
Author | Message |
---|---|
|
Clean code that works... how to?
Posted: Mon May 30, 2016 7:27 pm
|
I have taken a detour from developing Swabby for the SMS (http://www.smspower.org/forums/16052-SwabbySMSVersionDevelopmentThread), because I need to get some education regarding how to structure source code well. When I was coding an enemy wave generator for the Swabby game, I realized that I was again heading down the road of unclear dependencies, vague/bloated functions and generally creating code that would be hard for me to maintain and improve as the project grows. Then I have actually spent quite some time learning about refactoring, working with legacy code, etc. I have even gone so far (?) as to try my hand at a very simple, macro-based unit test framework for SMS test driven development. I read about code katas, and tried to make a clean-code-that-works bowling game scoring system, and.. blah blah.
Anyway, I hope some of you will contribute selected tips and principles you use when developing for the SMS. In the vein of this topic (http://www.smspower.org/forums/15794-AFewHintsOnCodingAMediumLargeSizedGameUsingWLADX), but with emphasis on code structuring. Nothing is too lame or basic. Let me start: * Make precise, descriptive names for your subroutines/functions. * Subroutines do something - their names often start with a verb, i.e. initializeBuffer, createObject * If a subroutine grows and performs multiple duties, it might be good to split it up into two or more separate subroutines, each clearly named and with a well defined job to do. * Strive to never hardcode values. Clearly separate code and data, and feed data to code through well defined routines using either the registers, the stack and/or variables as arguments. * Don't repeat yourself. Remove duplication (with the exception of unrolled loops, I guess) * And..? :) |
|
|
Posted: Mon May 30, 2016 7:56 pm |
Right, and I usually use verbs that would give me also an idea about if the functions interact with screen (tilemap/sprites) or VRAM in general, or they initialize game maps/arrays. So I name them 'drawSomething', 'loadSomething', 'prepareSomething'. It isn't perfect, but it's a further step :) |
|
|
Posted: Tue May 31, 2016 2:55 am |
Assembly or C ? | |
|
Posted: Tue May 31, 2016 4:58 am |
Agreed! I have my trusted old loadSAT routine that does exactly what it says it will do :)
I'm working with assembly. |
|
|
Posted: Tue May 31, 2016 7:36 am |
Code management is annoying.
Assembly puts additional twists on the topic as code is generally unintuitive to the human eye, difficult to ascertain in its purpose. On oldschool hardware measures to improve readability / scale-ability can be punitive to performance (modern day coding practices, implementation of higher level languages, etc...). My opinion is where possible and feasible try the following; - accept sms assembly will be a little messy, don't try to make it perfectly clean. - put common functions in to routines / macros with clear names, vdp functions are obvious choice here. - try to have calls including interrupt handler respect hardware state (return with same register values, etc). - not all code is equally complex, apply code documentation relative to code complexity. - document what is happening from perspective of problem domain. - find coding / documentation format that works for you. be consistent with it within projects. On the subject of macros I feel it is better to aim for broad application over narrow application. For instance the following macro... WriteToVRAM <data location>, <vdp address>, <bytes> ... can be used for ... ; write buffered data to vdp sat WriteToVRAM _satBuffer, _vdpSAT, 256 ; write title screen name table WriteToVRAM NATTitleScreen, $3800, NATTitleScreenEnd - NATTitleScreen Pivoting a little low level project management i would also suggest the following. - manage complexity. Greater complexity = harder to read + greater chance of bugs. Not everything has to be 100% optimal. Apply common sense and readability, you can always come back and optimize. - compartmentalize problems where possible (and be cautious when you do not)! The combination of two or more problems is usually more complex than dealing with those problems in isolation. |
|
|
Posted: Tue May 31, 2016 9:14 am |
One thing I tend not to do, but may make sense to many, is to split the source into multiple files. This enforces some separation of the structure, and makes it harder to make spaghetti code. However, personally I find it easy enough to split things up within a single file (or maybe a few non-project-specific includes), and then it's a lot easier to search in a dumb text editor.
I would endorse using sections in WLA DX, local labels wherever possible, long label names (no updsatbuf please) and loads of comments. Where appropriate, document the registers that are in/out/trashed by your functions. |
|
|
Posted: Thu Jun 02, 2016 7:19 pm |
Thanks for the feedback guys. It confirms most of my own thoughts on the subject of code management, and it have started some reflections on how to proceed with my current project. Anyway, all that is on hold at the moment since an extremely hot (early) Danish summer almost makes my laptop melt these days. | |
|
Posted: Thu Jun 02, 2016 8:30 pm |
As for naming conventions, I tend to use reverse names, I mean, first the thing, then the action. It seems more natural to me, at the first glance you know where to peek if you need to see what de function does or what's in action or whatever.
I mean this: player_init, player_kill, player_update, player_render, etc. enems_init, enems_update, enems_render, etc. screen_draw, screen_redraw, screen_flick... Of course, all related functions go in the same file. Anyways, I try to create clean and readable code, but many times you need to get your hands dirty to shave 100 bytes or stretch a bit to fit in the frame time. |
|
|
Posted: Sun Jun 05, 2016 9:37 am |
Good point, na_th_an! I'm experimenting a little with using wla-dx structs to allocate ram. Using periods in the names, you can have something like:
player.x player.y player.init (subroutine) It looks ok, but maybe the similarity to object oriented programming ends up being counterproductive. psidum, your point about compartmentalising problems holds very true to my code management challenges: I'm often trying to build a low-level engine at the same time I'm thinking about gameplay elements etc. To a certain extent this is unavoidable (and I like the freedom), but I think I need to focus more on how to settle some engine level decisions before moving on... From a lower level to a higher level. |
|