ID:120919
 
Be sure to check out:
Programming Tips #1 - If Statements
Programming Tips #2 - Making Progress
Programming Tips #3 - Design
Programming Tips #4 - Datums
Programming Tips #5 - Organization
Programming Tips #6 - Procs & Organizing Code
Programming Tips #7 - Comments and Whitespace
Programming Tips #8 - Loops and Ifs


When you first start programming, the problem you're trying to solve is "how do I write code that'll do ______?". Initially you're happy just to get something working, who cares what the code looks like. But when you try to develop a complete game this indifference can become a problem. As your code gets messier and messier, the project becomes harder to work on - you're more likely to write code that has bugs and they become harder to track down and fix. Eventually it'll get to the point where you're wasting a lot of time, don't feel productive, and are more likely to stop working on the project.

As a more experienced programmer, instead of asking "how do I do ______?", you should start asking "what's the best way to do ______?". In this post we'll look at some techniques you can use to keep your projects organized.


Using the Map Editor


BYOND's Dream Maker program gives you a map editor for creating your game's maps. You don't have control over its features, but you do have control over the objects you're placing on the map. You define what objects can be placed on the map and you define what these objects do when the game starts up. You can use both of these things to your advantage to simplify how you use the map editor.

Here are some tips and techniques I've found that make the map editor easier to use.

1. Instead of using multiple object types or the instance editor, use the object's constructor.

The instance editor is difficult to use and it's hard to manage the instances you've made changes to. Having multiple object types lets you avoid using the instance editor but can really bloat how many object types you have (which can also make things hard to manage).

Suppose you want to use four different grass icon states to make things look less tiled. You could create a single object type (ex: /turf/grass) and use the instance editor to change it's icon state, but this would be very annoying. You could create four different grass types (ex: /turf/grass1, /turf/grass2, etc.) but this makes your object tree bloated.

You can use the object's constructor to pick a random state. Because you just want some variation and don't care about assigning specific grass states to specific tiles, this is fine:

turf
grass
icon_state = "grass"

New()
..()
dir = pick(NORTH, SOUTH, EAST, WEST)

The grass icon state is a 4-directional icon where each direction is one of the variations. This way we're not crowding the icon file with tons of icon states.

The object's constructor can also be used to create autojoining tiles. Instead of having tons of different turf types that are used to create walls or floor types that use different icon states for different borders, you can write code that figures out which icon state should be used. Adding a little bit of code to handle this can really simplify map creation.

For example, in the Sidescroller library the walls have one pixel black borders around the edges. There are 16 different icon states for these walls. If I created 16 different object types and had to place them properly when creating maps, it'd take forever to make a simple map. Instead, I just use this code:

turf
wall
density = 1
icon_state = "wall"

New()
..()
var/n = 0
var/turf/t = locate(x,y+1,z)
if(t && istype(t,type)) n += 1
t = locate(x+1,y,z)
if(t && istype(t,type)) n += 2
t = locate(x,y-1,z)
if(t && istype(t,type)) n += 4
t = locate(x-1,y,z)
if(t && istype(t,type)) n += 8
icon_state = "wall-[n]"

Tiny Heroes uses a similar approach except the black outline is an overlay.

2. Be mindful of how many object types are dervied from the same parent type.

Being able to close and expand nodes of the object tree lets you limit what objects are shown in the map editor. If you tons of turf types that are all just children of /turf you can either show them all or hide them all, there's no in between.

If you wanted to create four different grass states and have control over where each type is placed, you could do it this way:

turf
grass1
grass2
grass3
grass4

But then you're bloating the object tree. If you have different types of grass, each with four different states, things can get really out of hand. Instead you can do it this way:

turf
grass
grass1
grass2
grass3
grass4

That way can keep /turf/grass collapsed when you don't need to use the other four types.

3. Remember that you can easily change maps by changing the objects you've defined.

If you make a new water icon you can easily update all water turfs to use the new icon by changing the turf's definition. This is obvious, but some of the ways of applying this idea are not as obvious.

Suppose you start working on a game and have just a single water icon, so you define this turf:

turf
water
icon_state = "water"

Then you create a bunch of maps that all have water and use this same turf type. However, later on you decide to make different tilesets and end up with six different water icons. Only a small amount of water tiles will continue to use the original water icon. There's no easy way to modify the turf's definition to make these changes because there's only one type, you'll have to define additional types and manually update maps.

Instead, you could have defined turfs like this from the start:

turf
tileset_1
water
icon_state = "water"
tileset_2
water
icon_state = "water"
tileset_3
water
icon_state = "water"
tileset_4
water
icon_state = "water"
tileset_5
water
icon_state = "water"
tileset_6
water
icon_state = "water"

Even though these all have the same icon, when you create the additional graphics for each tileset you can udpate these definitions as a way to easily update all maps.

4. Use areas to augment an object's constructor.

In Spies, windows are objs placed on the map. Each window needs to have its dir var set so it's facing outside. I could create four sub-types of window, one for each direction it can face. Sometimes this is a fine solution, but sometimes it's not. Instead, I can do this:

area
direction
icon_state = "direction"
layer = TURF_LAYER + 1

New()
..()
icon = null

north
dir = NORTH
south
dir = SOUTH
east
dir = EAST
west
dir = WEST

obj
window
New()
..()
var/turf/t = loc
var/area/a = t.loc

if(istype(a, /area/direction))
dir = a.dir

In the map editor I place the direction areas on the map to control what direction each window object will face. This makes the directions easy to change because I just have to change the turf's area (instead of digging around in the object instance editor). It also gives me a clear visual indication of what direction each window will have.

You can also use the turf's area to determine if something should or shouldn't happen. For example, Tiny Heroes adds black outlines around the edges of dense turfs. These are added at runtime so I don't have to manually add them in the map editor. However, checking all turfs to see which ones need to be outlined can take a long time.

I create an area called /area/outline and place it on all of the edges. At runtime, the game only checks turfs inside that area when adding outlines. This saves a lot of time and still has a minimal impact on how long it takes to make maps.

Another way you could apply this is with shadows that are generated at runtime. For example, in this image from a Dragon Warrior game, walls cast shadows. To the right of every wall tile is a shadow. You could place these on the map editor yourself, but that'll take a lot of time. Instead you can add shadows in the turf's constructor.

The shadows are easy to generate because they follow some simple rules. The problem is, what if there are exceptions to the rules? If there is a case where you want to manually place shadows on the map, you need to have a way to tell the turf's not to generate their own shadows. You can do something like this:

area
manual_shadows

turf
New()
..()
if(!istype(loc, /area/manual_shadows))
// generate shadows

That way you can place /area/manual_shadows on each tile you want to manually place shadows on.


You can't directly control how BYOND's map editor works, but this article shows that the map editor can be easy or difficult to use based on things that you do have control over. I gave a few examples here that I've found to be helpful, but I'm sure there are tons more. When you find that map editing has become a hassle, look for ways that you can code or define objects different to make map editing easier.

Maps can be very important to your game and you will often spend lots of time working on them. These tips will help you be more productive so you spend more time building and improving maps rather than spending all of your time editing the properties of a single object instance.
#10 will be about variable naming conventions.
#11 will tie together ideas from multiple articles
#12 might be about design documents

After that I'm out of ideas. I've been trying to avoid writing articles about super specific topics. These articles are about how to program. They're not about how to implement a specific feature (though they contain specific examples), these articles are about larger, more general concepts that can be applied to whatever you're working on. Instead of writing an article about a specific topic I'd make a demo or library instead.

As always, ideas for more topics are very much appreciated.
Two subjects that seem very relevant to our developer community in particular:

#13: What is good code and what are the standards used to measure good code? When and how does this differ from 'efficient' code?

And maybe #13.5: When should we care for efficient code and what should be sacrificed for it? When it comes down to it, what makes code efficient or inefficient? (maybe introduce big O notation to illustrate the point)
Thanks for the suggestions!

Toadfish wrote:
#13: What is good code and what are the standards used to measure good code? When and how does this differ from 'efficient' code?

I'm not sure what you mean by that. Most of these articles show examples of good and bad code. Article #6 is probably the most focused on that topic, but it's hard to say in a short article what makes code good. I guess I never explained what we were looking for in code, but I hope it became clear that readable and maintainable code is better.

And maybe #13.5: When should we care for efficient code and what should be sacrificed for it? When it comes down to it, what makes code efficient or inefficient? (maybe introduce big O notation to illustrate the point)

If afraid to get into efficiency. If I did, I'd make it very game specific. Ways to optimize AI or something like that. People on BYOND are too concerned about lag to begin with, so while it's a good topic I think it might just lead to more confusion.
It might be a bit too specific, but I'd like to see more like this -- they provide a lot of insight into how you can minimize programming effort and get the most benefit out of it. It seems like it would be more of a combination of your articles, though.
DivineTraveller wrote:
It might be a bit too specific, but I'd like to see more like this -- they provide a lot of insight into how you can minimize programming effort and get the most benefit out of it. It seems like it would be more of a combination of your articles, though.

Some of these articles (this one in particular) are more practical than others. The theoretical ones are easier to come up with ideas for, but I'll try to come up with some more practical ones.
Forum_account wrote:
Thanks for the suggestions!

Toadfish wrote:
#13: What is good code and what are the standards used to measure good code? When and how does this differ from 'efficient' code?

I'm not sure what you mean by that. Most of these articles show examples of good and bad code. Article #6 is probably the most focused on that topic, but it's hard to say in a short article what makes code good. I guess I never explained what we were looking for in code, but I hope it became clear that readable and maintainable code is better.

From what I've seen, you've gotten into good programming practices that make code clear and readable but have not discussed internal properties of the code itself very much. Introduce standards with which to measure good code other than efficiency (robustness, flexibility, ...) and explain when each of these should be given weight.

I like Article #6 but it seems too elementary and broad to be very useful. We have a lot of articles aimed at the lowest level but very few to appeal to people who have some idea what they're doing but aren't quite there yet - these are the people who made all those anime games you see on the hub.

And maybe #13.5: When should we care for efficient code and what should be sacrificed for it? When it comes down to it, what makes code efficient or inefficient? (maybe introduce big O notation to illustrate the point)

If afraid to get into efficiency. If I did, I'd make it very game specific. Ways to optimize AI or something like that. People on BYOND are too concerned about lag to begin with, so while it's a good topic I think it might just lead to more confusion.

Don't get into 'how to optimize' but 'why to optimize' and 'when it matters'. We have a lot of people worrying too much about conserving two + statements inside their for() loop than we should have (and, interestingly, not blinking an eye that they actually have 9 nested for loops). It's exactly as you said: "People on BYOND are too concerned about lag to begin with". Explain why they shouldn't be, and what systems or aspects of your code should you focus on when you actually need to optimise.
I hope you'll accept a little criticism, as an elaboration of an issue I mentioned in my previous post. These articles are very focused on having wide appeal but as a result they lose a lot of utility beyond the most elementary level. While there are most likely people who benefit from them I think you could worry less about specificity and strive for more practicality. Don't want to get so specific as 'how to optimize RPG AI' but also not be so devoid of content as self-motivation books. Probably the articles which've had, single-handedly, the most influence among developers here are an article dealing with vectors by Hobnob back in 2008-ish, and a 4-part series dealing with the then new interface file by Lummox around 2006. These articles were aimed at people already capable of developing games but who want to improve their craft - rather than complete beginners, at whom the vast majority of articles in our community seems to be aimed (as a side-note, I think, historically, the most influential libraries were Deadron's Text Handling, Lummox's Swap Maps, and lately your pixel movement library).
Toadfish wrote:
These articles are very focused on having wide appeal but as a result they lose a lot of utility beyond the most elementary level. While there are most likely people who benefit from them I think you could worry less about specificity and strive for more practicality.

You can't really have one without the other, but I do agree that I should mention more practical applications of these concepts. Practical topics are often served better by a library or demo. It's hard to find topics that are more practical than these, but not too practical that it's better to just write the library or demo to explain the topic.

Thanks for the feedback. It does give me some ideas for how I can work in more practical topics.

As for these articles, there were several reasons why I wanted to explain these topics. Most BYOND users fall into three categories when it comes to programming knowledge:

newbie - is still working on understanding the DM language's syntax
novice - has a firm grasp on DM and seems to be a productive game developer, but still isn't capable of writing good code
intermediate - makes a conscious effort to improve the quality of their code

Most BYOND users are newbies or novices. Those who are intermediate are usually there more by luck than skill - they're the people who write decent code but aren't sure what it is that makes it decent. They developed some good habits without actually being aware of the concepts they're applying, so they're not able to push these concepts any further. These articles are meant to:

1. Give novices a clearer path toward the intermediate stage.

2. Show people who think they're intermediate (and are, but didn't know why) exactly what they're doing that makes them intermediate.

3. Show people who are novices but think they're intermediate that they are indeed just novices.

These articles were aimed at people already capable of developing games but who want to improve their craft

That's who these articles are aimed at too. This stuff sounds elementary but a lot of people don't get it. It's not that these developers are incapable of understanding these topics, it's just that they're self-taught so they never thought about these things. The first article in this set was a little on the basic side, but it shows that there are things that seem obvious and are easy to understand, but many people wouldn't have thought to do it on their own.

These articles were heavily inspired by specific BYOND users. I won't name names (though you're welcome to make guesses), but here are descriptions of the habits I was hoping to address:

1. People who frequently blog about progress updates for their new game but with each update you can see they're losing steam and tend to never get games finished.

2. People who are even better programmers than #1 but don't blog about their game as much because they'd rather not have people knowing they failed when their game never comes out.

3. People who pretend to make progress on their game by finding trivial ways to implement things that seem impressive because they lack the ability to manage a moderately complex project but want to give the illusion they can (and are).

4. People who are decent game developers but are self-taught and have become set in their ways because they base their habits on their experience. Their development has a low ceiling because they think "what has worked for me?" rather than "what's a better way to do this?". Unfamiliar methods are avoided because they're not tried and true.

A lot of the problems I see are conceptual. A practical article about vectors might be useful to these people but it doesn't fix the underlying problems which continually keep them from finishing games (or from reaching their potential, since some of them occasionally finish games).

It's exactly as you said: "People on BYOND are too concerned about lag to begin with". Explain why they shouldn't be, and what systems or aspects of your code should you focus on when you actually need to optimise.

It's an irrational fear of lag and it's hard to explain things to irrational people. I'm willing to surrender the point. The only problem is that it seems like if everyone is concerned about lag, no matter how irrational it is, the BYOND staff will also be concerned about lag.
Thanks for responding :). You make some interesting points. I've changed my mind about where I'd like to see these articles headed towards after reading your post, and my opinion is now this:

I think there is a distinction between motivating people to create games, and teaching them how to program properly. While the latter is certainly part of the former, it seems to have a relatively small effect on the total developer output: most of our decent programmers don't, for whichever reason, finish their games, whereas it seems certain motivated novices can chunk them out by the dozen.

Analogical to this: the articles which have had the most benefit on actual game creation in the past were articles which gave the reader practical theory to work with (the articles that 'got things done'): vectors, random map generation, HUDs, .... These articles did little to improve the programming habits of the user, but they're 'shiny' enough that novices who were previously unmotivated to finish a game suddenly had a surge of motivation to apply the tools they've learned in some game. However, this spectrum of articles motivates very simple, low-standard games that are often closer to 'technical demos' than actual games. This is why we need theoretical articles, which is what you're doing.

My criticism, I suppose, is that separating practicality from theory in this manner is quite inefficient: the practical articles are perhaps too practical to teach the reader good programming habits, but the theoretical articles are abstract enough that the novice reader has a hard time making something out of them. They're not 'cool' enough that I'd want to immediately rush to DM and try out what I've learned (OK - organizing code is important, but this Ninjutsu I've been wanting to do since last week is more tempting than restructuring my source code without practical benefit). In order to motivate both good programming habits and game creation, I think you need a more 'hands on' approach, e.g. program a complex, relevant-to-game-making system from the grounds up and show what practical benefit applying the concept you want to introduce gives you. Exemplify abstract concepts in footnotes to a practical tutorial. For example, rather than giving AI as an unfinished, abstract example in your datums article: call your datums article 'How datums can help you program AI'. Make it something relevant and practical that can anecdotally teach theoretical concepts.

Now, instead of thinking 'why would I care about a cluttered "object tree" when I could immediately begin mapping my Ninja Village?' I'm thinking "Wow, if I properly organize my object tree using cleverly conceived procs, variables and datums, players will have an easy time using the map maker to create dungeons with custom enemies for me to put in the game!". Your article introduces both theory and the tools it births. This is an ambitious task, but it will have the most influence all in all. You can see this, because every time you asked for new articles ideas, the vast majority of suggestions were practical tools people were interested in. In comparison, there hasn't been much of a response to Comments and Whitespace, Organization or Design.
Toadfish wrote:
most of our decent programmers don't, for whichever reason, finish their games, whereas it seems certain motivated novices can chunk them out by the dozen.

I'm making huge guesses at what the problem is, but...

The motivated novices don't worry about little details so they're able to get things done (to some extent). The decent programmers advanced past the novice stage because they became aware of the details they used to overlook (ex: designing code to be flexible). To make up for the time spent being a novice, these developers feel like they have to address every little detail, end up overcompensating, and bite off more than they can chew. They're at the point where they know what needs to be done but don't have the experience or ability to manage getting it all done.

My criticism, I suppose, is that separating practicality from theory in this manner is quite inefficient: the practical articles are perhaps too practical to teach the reader good programming habits, but the theoretical articles are abstract enough that the novice reader has a hard time making something out of them.

My concern is that an article that has a mix of practicality and theory will end up being interpreted as purely practical. The articles I've written are highly theoretical and I've had people mistaking them for being practical!

I think I'll eventually write more practical articles that link back to these articles. That way the practical article only needs to briefly mention each concept. The concepts won't be missed or glossed over because it's clear that each one has an entire article devoted to it, but this large amount of theory doesn't bog down the practical content.

The direct benefit of the practical article is that people learn how to do whatever the article was about. The indirect benefit is that the article makes people think and expands their general programming knowledge so they can later make seemingly unrelated advancements. My concern is that going for the direct benefit seems better suited by libraries and demos and that going for the indirect benefit is too much of a gamble.

One of my initial goals with making libraries was to eliminate the "technical demos". Having a library that does something for you trivializes the task and makes the demos unimpressive. I expected that people would stop making tech demos and use the libraries to make games, but I'm not sure if either is really happening. I'd like to get people to work on games and make significant progress but I'm not sure how. Most people seem determined to be unproductive.
Forum_account wrote:
Toadfish wrote:
most of our decent programmers don't, for whichever reason, finish their games, whereas it seems certain motivated novices can chunk them out by the dozen.
The motivated novices don't worry about little details so they're able to get things done (to some extent). The decent programmers advanced past the novice stage because they became aware of the details they used to overlook (ex: designing code to be flexible). To make up for the time spent being a novice, these developers feel like they have to address every little detail, end up overcompensating, and bite off more than they can chew. They're at the point where they know what needs to be done but don't have the experience or ability to manage getting it all done.

That, or we simply get too lazy until we have "even better idea" to work on for few weeks(I do this all the time. Can't get rid of this habit.)
I wasn't sure where to post this, but I figured you'd get the alert anyway. It kinda relates to this topic, I think.

I find it strange how you always do your outlines. It makes more sense to me to have an outline's icon state describe the borders it touches; 1 for north, 2 for south, 3 for both, etc. It works the same exact way and it makes more sense to me than what you do.
What you do does make sense (it's clockwise starting from north), but considering the BYOND-directions are actually associated with their own numbers, it makes more sense to me off the top of my head to do this:
var result = 0, d = 0
for(var/n in 0 to 3)
d = 1 << n
result |= istype(get_step(src, d), type) && d
overlays += image('outline.dmi', "[result]", layer=FLOAT_LAYER)

Although it may contradict what you said about being super-clear in your code (walk away for 2 weeks, come back, and try to understand it), it's actually not hard to read if you read it as carefully as you need to: we're looking for a binary integer 'result', looping through all cardinal directions 'd' to check adjacent turfs, and ORing that direction to the result if the adjacent turf matches.
The code you posted there is fine. In demos which are this simple it'd work, but in other projects it often becomes more complex. I'm checking each of 4 neighbors and adding a drop shadow to the tile below if it's non-dense. Ugly code with binary operations doesn't lend itself to these kinds of changes. Using BYOND's values for the directions also limits you to 16 states.

In Tiny Heroes tiles were 32x32 but with 16x16 blocks. The overlay had multiple icons - 256 states for the outer edge of the 32x32 icon (each side was split into two parts) and a 16x16 overlay for the smaller blocks. For this there's a 29-state outline and a 47-state shadow.