fluffrabbit {l Wrote}:it should all be doable in a matter of months rather than years.
Lyberta {l Wrote}:fluffrabbit {l Wrote}:it should all be doable in a matter of months rather than years.
Well I've started in 2010 with my own engine. So far I have:
- Basic skeletons of most subsystems
- Ability to draw pixels, single pixel lines and rectangles
- Basic networking (no client-side prediction, no server-side lag compensation)
Here's my TODO list so far:
- Unicode (2 years)
- WebAssembly interpreter (1 year)
- Audio (1 year?)
- Virtual file system (maybe less than a year)
- Basic graphic primitives (no idea)
- GUI (3+ years)
- Level editor (no idea, a lot I guess)
- Physics (2 years)
- Turning command line interpreter into Turing complete language (1+ year)
- Writing actual game
So that's about 20 years total for an engine and who knows how much for the actual game.
Audio (1 year?)
scameron@wombat ~/github/wordwarvi $ git show 804f70de7a4146abcccfbfea3e91888b775ad9b7 | diffstat
Makefile | 2
wordwarvi.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 216 insertions(+), 1 deletion(-)
scameron@wombat ~/github/wordwarvi $ git show 804f70de7a4146abcccfbfea3e91888b775ad9b7 | head
commit 804f70de7a4146abcccfbfea3e91888b775ad9b7
Author: smcameron <smcameron>
Date: Tue May 8 00:36:13 2007 +0000
Added very very simple sound. Now needs portaudio and libsndfile
(Doesn't actually use libsndfile yet though.)
diff --git a/Makefile b/Makefile
index 0372ab4..083ce0d 100644
--- a/Makefile
scameron@wombat ~/github/wordwarvi $
fluffrabbit {l Wrote}:Better to build a clusterfuck nightmare codebase with the bits you need as you go along.
fluffrabbit {l Wrote}:In particular, you don't need advanced scripting all over the place. WASM is good to investigate, but the STB C lexer, V8, LIL, Squirrel, or another interpreter might serve you better.
fluffrabbit {l Wrote}:Virtual file system? You don't need that either. You already have code to put things in the right places, and Emscripten is a special case that gives you access to your main data folder as a virtual file system. Just use the regular file system.
rubenwardy {l Wrote}:Your problem here is making so much of this yourself, rather than using existing libraries like SFML, enet, and engines like Godot.
smcameron {l Wrote}:Audio (1 year?)
1 year seems pessimistic, esp. if you use something like portaudio (which is still pretty low level -- you still have to write your own mixer code with portaudio) then it shouldn't take more than a couple weeks or maybe a month to get something going. When I did it back in 2007 (knowing *nothing* about audio programming when I started) for wordwarvi it amounted to approximately 500 lines of code
smcameron {l Wrote}:The way I approach big things like this is for each little part, I kind of throw together the smallest version of it that will basically work. That doesn't mean I don't think about the API and try to come up with something good, it means I try to identify the minimum feature set that will let me mostly do what I'm trying to do, and I implement just that
rubenwardy {l Wrote}:If you want to be more productive, you need to be able to accept things that aren't done exactly the way you like
rubenwardy {l Wrote}:You shouldn't need to be writing an audio library at all, it's supposedly a solved problem
rubenwardy {l Wrote}:One of the primary benefits of C++ is that it's built on C, so you can use C libraries easily.
rubenwardy {l Wrote}:With well designed code, you abstract away such code. For example, only one class in my entire game even includes enet - the Socket class. Encapsulation like this helps when you want to change implementations, as I did when I switched from my own sockets implementation to enet.
rubenwardy {l Wrote}:I understand the trap of reinventing the well, I - like most developers - have fallen into it too. But to be productive, you need to be more pragmatic. It depends on your aims and objective though. If you just want to learn about everything, then reinventing everything is a good approach for that. But if you want to one day release a game, you should spend your time on actually making that game
onpon4 {l Wrote}:Just as an example, consider Unicode support. You could spend months on end trying to get your game perfectly compatible with all the Unicode standards. Or, for instance, you can do what I did with Project: Starfighter, just code in UTF-8 support, hardcode that as the supported encoding, and leave it at that for now, because the only reason you need Unicode support is so that the game can be translated properly.
fluffrabbit {l Wrote}:Godot might not be a bad choice, except you have to program in GDScript rather than C++.
Lyberta {l Wrote}:So I swore to never code in any domain specific language ever and only use general purpose language so I can reuse my code for the rest of my life. So no GDScript or any engine-specific script ever.
the biggest reason I want Unicode are player names and chat.
How do you access a "character" in a string? You need a complex algorithm to do that.
How do you reverse a string? You need a complex algorithm to do that.
How do you sort strings? You need a very complex algorithm to do that.
How do you split string into words/lines? You need a complex algorithm to do that.
How do you convert string to lowercase/uppercase? You need a very complex algorithm to do that.
>>> s = "初めまして!Abbyです。お名前は?"
>>> print(s[2])
ま
>>> print(s[::-1])
?は前名お。すでybbA!てしまめ初
>>> print(''.join(reversed(s)))
?は前名お。すでybbA!てしまめ初
>>> print(''.join(sorted(s)))
Abby。おしすてではまめ初前名!?
>>> print(s.lower())
初めまして!abbyです。お名前は?
>>> print(s.upper())
初めまして!ABBYです。お名前は?
try:
from uniseg.linebreak import line_break_units
USE_UNISEG = True
except ImportError:
USE_UNISEG = False
#...
def f_split_text(self, text, width=None):
# Split the text into lines of the proper size for ``width`` and
# return a list of the lines. If ``width`` is None, only
# newlines split the text.
lines = text.splitlines()
if width is None:
return lines
else:
split_text = []
for line in lines:
if self.rd["font"].size(line)[0] <= width:
split_text.append(line)
else:
if USE_UNISEG:
words = list(line_break_units(line))
jchar = ''
else:
jchar = ' '
words = line.split(jchar)
while words:
current_line = words.pop(0)
while self.rd["font"].size(current_line)[0] > width:
start = ""
while self.rd["font"].size(
start + current_line[0])[0] <= width:
start += current_line[0]
current_line = current_line[1:]
split_text.append(start)
while (words and self.rd["font"].size(jchar.join(
[current_line, words[0]]).rstrip())[0] <= width):
current_line = jchar.join([current_line,
words.pop(0)])
split_text.append(current_line.rstrip())
return split_text
int gfx_renderUnicodeBase(const char *in, int x, int y, int real_x, int fontColor, int wrap, SDL_Surface *dest)
{
SDL_Surface *textSurf;
SDL_Color color;
int w, h;
int avail_w;
int changed;
int breakPoints[STRMAX];
int nBreakPoints;
char testStr[STRMAX];
char remainingStr[STRMAX];
PangoLogAttr logAttrs[STRMAX];
int nLogAttrs;
int i;
SDL_Rect area;
if (strcmp(in, "") == 0)
return y;
avail_w = dest->w - real_x;
switch (fontColor)
{
case FONT_WHITE:
color.r = 255;
color.g = 255;
color.b = 255;
break;
case FONT_RED:
color.r = 255;
color.g = 0;
color.b = 0;
break;
case FONT_YELLOW:
color.r = 255;
color.g = 255;
color.b = 0;
break;
case FONT_GREEN:
color.r = 0;
color.g = 255;
color.b = 0;
break;
case FONT_CYAN:
color.r = 0;
color.g = 255;
color.b = 255;
break;
case FONT_OUTLINE:
color.r = 0;
color.g = 0;
color.b = 10;
break;
default:
color.r = 255;
color.g = 255;
color.b = 255;
}
if (gfx_unicodeFont != NULL)
{
strcpy(remainingStr, in);
if (TTF_SizeUTF8(gfx_unicodeFont, remainingStr, &w, &h) < 0)
{
engine_error(TTF_GetError());
}
changed = wrap;
while (changed && (w > avail_w))
{
nLogAttrs = strlen(remainingStr) + 1;
pango_get_log_attrs(remainingStr, strlen(remainingStr), -1, NULL, logAttrs, nLogAttrs);
nBreakPoints = 0;
for (i = 0; i < nLogAttrs; i++)
{
if (logAttrs[i].is_line_break)
{
breakPoints[nBreakPoints] = i;
nBreakPoints++;
}
}
changed = 0;
for (i = nBreakPoints - 1; i >= 0; i--)
{
strncpy(testStr, remainingStr, breakPoints[i]);
testStr[breakPoints[i]] = '\0';
if (TTF_SizeUTF8(gfx_unicodeFont, testStr, &w, &h) < 0)
{
engine_error(TTF_GetError());
}
if (w <= avail_w)
{
textSurf = TTF_RenderUTF8_Blended(gfx_unicodeFont, testStr, color);
if (textSurf == NULL)
{
printf("While rendering testStr \"%s\" as unicode...\n", testStr);
engine_error("Attempted to render UTF8, got null surface!");
}
area.x = x;
area.y = y;
area.w = textSurf->w;
area.h = textSurf->h;
if (SDL_BlitSurface(textSurf, NULL, dest, &area) < 0)
{
printf("BlitSurface error: %s\n", SDL_GetError());
engine_showError(2, "");
}
SDL_FreeSurface(textSurf);
textSurf = NULL;
y += TTF_FontHeight(gfx_unicodeFont) + 1;
memmove(remainingStr, remainingStr + breakPoints[i],
(strlen(remainingStr) - breakPoints[i]) + 1);
changed = 1;
break;
}
}
if (TTF_SizeUTF8(gfx_unicodeFont, remainingStr, &w, &h) < 0)
{
engine_error(TTF_GetError());
}
}
textSurf = TTF_RenderUTF8_Blended(gfx_unicodeFont, remainingStr, color);
if (textSurf == NULL)
{
printf("While rendering remainingStr \"%s\" as unicode...\n", remainingStr);
engine_error("Attempted to render UTF8, got null surface!");
}
area.x = x;
area.y = y;
area.w = textSurf->w;
area.h = textSurf->h;
if (SDL_BlitSurface(textSurf, NULL, dest, &area) < 0)
{
printf("BlitSurface error: %s\n", SDL_GetError());
engine_showError(2, "");
}
SDL_FreeSurface(textSurf);
textSurf = NULL;
y += TTF_FontHeight(gfx_unicodeFont) + 1;
}
else
{
engine_warn("gfx_unicodeFont is NULL!");
}
return y;
}
int gfx_renderUnicode(const char *in, int x, int y, int fontColor, int wrap, SDL_Surface *dest)
{
int w;
if (x == -1)
{
TTF_SizeUTF8(gfx_unicodeFont, in, &w, NULL);
x = (dest->w - MIN(w, dest->w)) / 2;
}
gfx_renderUnicodeBase(in, x, y - 1, x, FONT_OUTLINE, wrap, dest);
gfx_renderUnicodeBase(in, x, y + 1, x, FONT_OUTLINE, wrap, dest);
gfx_renderUnicodeBase(in, x, y + 2, x, FONT_OUTLINE, wrap, dest);
gfx_renderUnicodeBase(in, x - 1, y, x, FONT_OUTLINE, wrap, dest);
gfx_renderUnicodeBase(in, x - 2, y, x, FONT_OUTLINE, wrap, dest);
gfx_renderUnicodeBase(in, x + 1, y, x, FONT_OUTLINE, wrap, dest);
return gfx_renderUnicodeBase(in, x, y, x, fontColor, wrap, dest);
}
So I swore to never code in any domain specific language ever and only use general purpose language so I can reuse my code for the rest of my life. So no GDScript or any engine-specific script ever.
if Godot Engine somehow breaks, just take the old version that works and fork it.
fluffrabbit {l Wrote}:Instead what happens is the API changes in undocumented ways (has happened with Godot before) and all of your code breaks. Then you have a choice of either forking Godot and fixing it (good luck with that one) or fixing your own code to match whatever the baka decided to insert into their game engine, fully expecting that you will have to do the same every couple of months if you want fixes for problems that were there when you started
onpon4 {l Wrote}:Or you can just reuse code that already does that. Python has most of this stuff as simple syntax and/or functions in the Python Standard Library:
- {l Code}: {l Select All Code}
>>> s = "初めまして!Abbyです。お名前は?"
>>> print(s[2])
ま
>>> print(s[::-1])
?は前名お。すでybbA!てしまめ初
>>> print(''.join(reversed(s)))
?は前名お。すでybbA!てしまめ初
>>> print(''.join(sorted(s)))
Abby。おしすてではまめ初前名!?
>>> print(s.lower())
初めまして!abbyです。お名前は?
>>> print(s.upper())
初めまして!ABBYです。お名前は?
Users browsing this forum: No registered users and 1 guest