Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    122
  • comments
    121
  • views
    69270

About this blog

My not-so daily posts on my current console project

Entries in this blog

 

Ah, me

Ah, me. How easily I get distracted, enticed by the thought of "Oh, that would be cool", or "I wonder if I can do this". The first thing I need to make sure I understand, right now, is that for pity's sake, I'm not trying to write a full-featured C++/Lua binding! [flaming]

With that out of the way, I may as well explain what I've done so far (and ultimately scrapped because I can do without it)... You've seen my methods-only object binding from my last post. I may keep that at least. However, you may also remember how I wanted to be able to bind variables, too. I did some experimenting with it, and in the end it did work, but it kills one particular assumption of Lua: variables are dynamically typed. Assigning a string to what once held an integer is acceptable. Not so with my variable binding, and there's no good way around it. Shall I explain? Why not.


All of Lua's lua_push* functions push the value of the variable you pass, not a reference to one. This is a good thing for plenty of reasons, but it also means you need to do some extra work to bind a variable. The only option you have is to store a pointer to the variable as a userdatum. Userdata in Lua have no inherent properties, though; metatables are applied to make them more interesting.

Unfortunately, when you access a Lua variable, the variable itself isn't told about this. It's the metamethods of its parent[/it] container that are notified instead. Specifically, __newindex (for setting) and __index (for getting). So the parent container needs to have these metamethods. It also needs some way to tell if the resource you're asking for is a bound variable and not some other kind of userdatum (or not even userdata at all), namely by checking the userdatum's metatable.

And even after all that, there needs to be some way to actually get/set that variable. These functions are implemented in the C side of things, and called with __index/__setindex are sure that it's a bound C variable that you're trying to get at. The final problem here is that C variables are strongly typed. Passing a string in where the bound variable expects an int, no, you'll get an error. And that's not expected behavior for Lua by any means. At this point I threw in the towel. If I'm implementing get/set methods for these variables, why do I bother at all? I can just bind methods using the approach from my last post and get the same effect, with less hair loss!


I probably rambled a bit there, but it's 2am, so I can't really expect much better of myself. Suffice to say, I can implement the snippet of goal code I gave in my last post in pure C binding rather than doing any C++ object stuff. Maybe I should if it's such a hassle. But what can I say? I like my OO. [sad]

Twisol

Twisol

 

Happy birthday to me!

I turned 17 today! I won't technically be 17 for maybe three more hours, but for all intents and purposes...

I've been working a lot on that Nucleus engine I mentioned previously. Right now I'm getting to know the Lua/C API so I can write a C module that Nucleus can load as a native extension to Java, as a Lua interop module. At the moment it's standalone for testing purposes, while I struggle to learn how to bind C++ classes/objects into Lua to my tastes. I'm happy with what I have so far, but right now it can only bind functions, not other kinds of values. It's probably worth writing down what I've learned so far though.


The goal: Access instance methods of a C++ object from Lua.
The problem: Lua can only easily bind static methods. There needs to be some way to tell a function what instance it should act on.
The solution (simple in hindsight): store the object instance as a full userdatum, and call its methods in Lua using the colon operator (object:method(params)). This is really just syntactic sugar for object.method(object, params). Given that object is the instance, object is really just 'this'.

The above is the keystone to the solution. Obviously it won't do much without some support. For example, we still have to bind a static function for Lua to call. Furthermore, userdata does nothing useful without a metatable. So we have to register a metatable to our new userdatum, with a (static) method registered as its __index at the very least. This function will handle lookups into the userdatum; if I do "object.foo", __index will recieve obj and "foo", and return an appropriate value, even if it's just nil. This is done with a static array of name/pointer pairs to allow lookups based on name.

We expect "object.method" to call __index and return that particular method. But remember, these methods are instance methods, and Lua can't call those directly. So instead of returning the method directly, we'll create a static 'thunk' function to resolve the access and call our instance method. We'll register this thunking function multiple times, once for each instance method, and registering a pointer to a data structure as an upvalue each time. The data structure just holds a pointer-to-member; even though pointers-to-member can't be casted to void*, a pointer to a vanilla struct instance sure can. This is basically just a Lua version of std::bind1st, creating a new object (closure) each time.

Now, "object.method" will call into __index and get back our simple thunking method, with a unique upvalue representing the method to call. So, calling this method with parameters (and using the colon syntax to pass a hidden 'this') should successfully resolve to a call to an object's instance method: "object:method(1,2,3)".


The code is... a bit ugly right now. I want to try to clean it up a bit before I make it public, and hopefully add support for __index returning things other than a function. This is a hobby project, so while I know I could just go the C route and go with static functions in separate .c files, I like working with objects, and it's fun to figure out how this is all supposed to work. [smile]

Thanks for reading!
~Jonathan


EDIT: May as well post what my goal code is on the Lua end. This is what I want to be able to do when the interop module is finished:


local server = atom.request("server/telnet")

server.handlers["connect"] = function(self, client)
local db = atom.request("database/mysql")
db:connect("localhost", "foologin", "barpasS")
db:use("blacklist")

local result = db:query("SELECT * FROM tbl WHERE IP='" .. client.IP .. "'")
if #result > 0 then
self:disconnect(client, result[1].reason)
end
end


Ideally, the server.handlers table/udata would use a __newindex metamethod to tell when something's being assigned, and register the hooks appropriately. I think it's pretty intuitive, and I'm almost positive I can do everything in this snippet. Give me some time, people... [grin]

Twisol

Twisol

 

At a Snail's Pace

So I haven't really gotten much done so far. I'm trying to get back into it, and I think I'm succeeding... slowly. I'm starting to think I should spend some time on a shorter side-project, though. At the very least it would boost my morale. [smile] So, I decided to use the webspace I got with my GD++ membership to set up a small sub-site. Mostly for fun, really.

Now, I decided I didn't really want it to look totally different compared to the rest of GameDev.net... so I hijacked the GameDev.net header bar and stuck it in my page. [lol] I don't think the staffers will get angry at me. After all, it's still on gamedev.net, under my membership space. But it's just a static .html page, so I stripped out the ad. There's plenty of ads on the main site, anyways. [wink] But if that upsets someone, let me know how I can fix it. [smile]

You can check out my sub-site here. You might also notice that I hijacked the journal title bit. [grin]



In related news, I got a new book on (X)HTML and CSS. It's more of a textbook, which is actually perfect for me, because I'm tired of getting books without decent exercises. This one has around four cases per chapter! [grin] Plus, it was released just this year, so I know it's current.

I took my first HTML class when I was nine years old, and I learned the basics and such. My site never really looked good, though. [lol] So I was browsing the local Borders and this book caught my eye: HTML and XHTML: 5th Edition - Comprehensive. I've only finished chapter 4 - I finish each of the exercises before going on, because I can - and I've already learned some new things. Take, for instance, the tag. It's placed in , and supplied with a href attribute which is effectively prefixed to every relative link in the page. I used it (with href="http://gamedev.net") to test my sub-site locally, yet with the glorious logo and images, without editing every single link myself to do so. [smile]



Don't worry, I'm not giving up on Cripes. I'm just taking a breather. [smile]
~Jonathan

Twisol

Twisol

 

Short update on Nucleus

Nucleus is moving along nicely, although for some reason I keep putting off the event queues in favour of other features that, while essential, really can wait a little while. [grin]

One such "essential" feature is support for loading external modules. This may seem obvious [lol], but until now my test modules have been in the same project, and thus loaded at compile-time for me. I did a little research into ClassLoaders, and found a standard URLClassLoader class. I can (indirectly) pass it a File object on construction, which means I can directly control where to look for classes without relying on the classpath.

The idea is to have a configuration file containing everything the Nucleus needs in order to run. One of the datums involved is a list of paths to look for modules in, which allows the user to decide exactly where the modules will be sought after. (The configuration file itself will be found either in the same directory as Nucleus, or passed along as a command-line argument)


These detour-ish additions are vital to Nucleus, because they give power to the user. You could run Nucleus with one configuration file as the "main" server, and another file as a "development" server. And I'm working on some so-called "hotloading" functionality, to allow modules to be reloaded at runtime. It'll certainly be interesting, that's for sure. [smile]

Twisol

Twisol

 

matt_j is gonna kill me

Sorry Matt... Cripes 2.0 is frozen indefinitely. [sad]

In slightly happier news, I've made quite a lot of progress on a new Java-based project of mine, called Nucleus. It's a modular, event-based system that heavily utilizes reflection, and the idea is to use it for MUDs. [grin]


Nucleus - The extensible MUD kernel
The idea behind Nucleus is to implement low-level details like networking and timers, basically supplying basic features, while also allowing third-party modules to be loaded for extra functionality. Each module runs in its own thread, protected from the other threads except through certain core mechanisms, such as events. And speaking of events, each module is backed by an event queue in the core, which calls the module's Main() method after it's done with its current event.

If a module really wants to run independantly of events, it can create a separate thread (or threads), but the Nucleus won't care in particular. [smile]

One of the planned modules is an interop module, acting as a binding between Java and another scripting language. There will be one interop module for any given language, and you can plug any of them into the Nucleus to allow part of the application to run in another language such as Lua or Python. This is the planned route to implementing MUD logic. [grin]You could even write your own language and binding module; I know many MUDs use a proprietary language for their scripting needs.


The Events System
One of the bigger obstacles for me was defining an acceptable events system between modules. Obviously, no module can be expected to accept any kind of event argument. After many days of thought, and one night of particularly hard thought, I came up with a pretty easy solution. There are three kinds of events in the Nucleus: Broadcasts, Events, and Requests.

Broadcasts are sent to every module. To allow this, I just create a Broadcast type in the core. Simple, because every module will have access to the core packages anyways. This could be used quite easily for input to a debug-logger module, which takes any broadcast it hears and writes it to a file. No module would be required to make any changes to accommodate the logger.

Events are sent from one module to another. This implies that the source module has prior knowledge about the second. This also restricts the kinds of events that can be sent: only ones the target module knows about. It's relatively simple from there to suggest that the target module define events it will accept, and have the source module import those events. The only part that's in the dark about the events themselves is the core, and it will solve this by looking for a @NucleusEvent annotation in module packages, reflectively loading their definitions as Class objects and storing them per-module.

Requests are much the same as Events, except that the target module is expected to return a value. Simply marking a named method with a @NucleusRequestable annotation is enough to trigger the core to load the proper definitions.


I want to put some code up here at some point, but right now I'm still working on actually implementing this system. [grin]
~Jonathan

Twisol

Twisol

 

AdLib scrapped

Mixed feelings on this one, though mainly I got pretty far ahead of myself. I've abandoned AdLib, because it was too much work and just not enough return, and I finally decided that, yes, it was indeed reinventing the wheel. I may as well explain what I had.

The idea was, you could take a generic page with some named placeholders in it, and replace those placeholders with values (either resulting from a CGI script or directly from Apache via GET and POST... which was why I wanted to do an Apache module). You might also have a definitions file somewhere else with blocks of content that you'd inline into the page (I'll give an example below). I called it "CSS for content", but the analogy "CSS is to styles as AdLib is to content" probably fits better, because you'd take oft-used blocks of HTML and place them in a file, and refer to them in a page if you wanted them in the page. I'll write out that example now...


~..Header..~

AdLib!

~..Header..~

~..Body..~
Hello, ~!!name!!~. This is an "AdLib test page." ?>
~..Body..~

~..Footer..~

~..Footer..~

The ~..placeholders..~ surrounding blocks of content define and name those blocks. So, given that definitions file, you might do something like this to create a page using those definitions.
~??Header??~
~??Body??~
~??Body??~
~??Footer??~
The ~??placeholder??~ signified an inlining of a definition, and a ~!!placeholder!!~ inlines a value rather than a definition (say, from GET or POST, or from some other method). It's probably not hard to see how the page would have looked.

Another idea I had was that the AdLib engine might not just be used for output like that, but also input. You'd have a definitions file (possibly the same one used for output), and it would parse the page, matching the page back into the content blocks and coming up with the ~!!placeholder!!~ values that were used on the page. Used that way, it wouldn't be very different from taking two identical papers with identical words, cutting out a few words on one, and placing it over the other so you could see the words used on the back page.

Actually, that idea is what made me start on AdLib in the first place... I had a file I needed to get stuff from - a really, really big file - that did not lend itself to typical parsing. But every "page" in the file had either one kind of information or another kind, so I could split it into parts like that. In the end, though, it would have been just too much effort for not enough return.


At least that's one project off my shoulders. [grin]
~Jonathan

Twisol

Twisol

 

A short interlude

My brain's starting to hurt from working on Cripes, heheh. Don't get me wrong, it's fun when I get things done (see the maze generator: that was pure win), but when there's some downtime I get in a slump. Luckily, I do have other things to distract me. They're not terribly gamedev-related, but I hope nobody minds if I devote a post to them! I promise I'll write some more about my Entity refactoring at the end.

Before we get into it, I is a leet hackerz. Okay, not really, but I was adding some of the GameDev forums to the My Favorites list, and I noticed that the dev journal forum didn't have a link. I grabbed the bookmark-URL-link for another forum and replaced that forum's ID with the devjournal forum (53), and voila. (Staffers: Don't kill me! *flee*)


Website Programming
Okay, so.. My father runs Daycare.com, among a bunch of other websites, and I've been doing some (paid!) work for him on it. One of the things he's having me do is take Excel and .csv databases of daycare listings and fix them up to be imported into his daycare listings database. phpMyAdmin has a handy import page which can accept CSV files, handily.

So I've done about seven or eight of these so far, and generally I have to parse the fields out into the proper format for his database. I immediately learned how to use Excel's Proper(), Mid(), Left(), and Right() functions, of course. Down the road, I learned to use If(), if there was a certain value in an original column so I could parse/merge it into another column that was going to be imported.

This last one, though, I hit on a revelation, and I thought it was pretty nifty. I realized that every cell is basically just a variable. Each cell can have a formula that stores a new value in the variable. In that sense, it's just like the kind of programming that I'm used to. So lets take an example.. There's a column, "AgeStartEnd", which holds the youngest and oldest ages a daycare would take. Each cell has values like "04W10Y", and you can probably tell what that means if you stare at it. This is where my epiphany hit me. Speaking in terms of this realization, I created a row-wise program (a series of formulas lined up in a row) which takes an input (in this case, "04W10Y" as an example) and results in an output (the last cell/variable in the program). In effect, the program split the value down the center ("04W" and "10Y"), parsed the ages into readable text ("04 weeks - " and "10 years"), removed the trailing zero if there was any ("4 weeks - " and "10 years"), and concatenated the two back together ("4 weeks - 10 years").

Quite neat, really. Oh, and never make me build IF-ELSEIF-ELSE ladders in Excel. Ever. I had to do that above, one rung each for D, W, M, and Y. Really, try doing that when the IF()'s form is IF(condition, if-true, if-false). The next rung in the IF would go in if-false, instead of being something relatively readable. I had to build the weekday converter (i.e. "MON" to "Monday") in a notepad file (with the godsend of indentation) to do it properly.

Hmm, I spent too long on Excel - it's growing on me, especially with my epiphany. I also did some nifty stuff with Apache's mod_rewrite. Given that it can't really do complex replacements like with if-else ladders to change stuff, I created one rewrite rule which captured state codes ("daycare.com/CA") and translated it to a daycare_listings.php?state_$1 page. The PHP page took that value, expanded the state code to the full state name, and redirected to full_state_name_daycare_listings.html.

Writing production code is definitely interesting.


Cripes 2.0
Alright, cripes! I'll talk about Cripes now (please, don't laugh at that). I'm going to figure out a resource manager somehow, because of this post by pulpfist. I'm surprised it didn't occur to me earlier, considering how I already knew that having a Corner with every state it could be, and having probably fifty of those corners, was slow. So that's going in as part of my Entity refactoring, probably near the beginning.

I also want to abstractify and extend my Entity class out somehow, into Mobs and Missiles. Obviously the EntityMap will have to handle both, and so it'll work with Entity pointers instead of owning the actual objects. My EntityFactory will also need to be fixed up to handle the construction of both. I'm thinking of working with some kind of pluggable factory design, here.

After all that's done, I'm going to see what I can do about collision detection. That'll be fun!... well, I had problems with it in Cripes 1.0, but hopefully I've done well enough this time around.


Comments please? I mean, I just wrote all this out for you! *beg*

Twisol

Twisol

 

User Input Hassles

I'm not actually sure how to proceed here. User input is easy enough on its own, but I need to continually Draw() at a certain framerate as well as watch for user input (and act on it wen it comes in). I created a Keyboard class, but I'm really not sure how to do this.

I want to draw, at the moment, once every 0.25 seconds. I also want to watch for user input constantly, and move the "camera" every time a key is pressed (but only allow them to move once every so often). And I don't want to continually loop, because I think that hogs CPU; I just want to do something if there's something to do, or it's time to draw.

The only thing I've managed to do, that works in some respect, checks the console input buffer every time through the loop and sets the appropriate element in an array of bools depending on what key event comes through. Then it draws, and Draw() checks the time elapsed between the last time we drew and now, and if we're good to go, it checks the arrow key booleans and moves accordingly. The problem here is that it only works if we held the key down when Draw() actually draws. If we hit an arrow key after 50ms since the last Draw(), and let go at 100ms since the last draw, it's like we never hit the key at all - Draw() doesn't recognize that we tried to move. And it keeps looping whether we have anything to do or not.

Obviously I'm new to handling keyboard input like this. I think WaitForMultipleObjects() could help (because it can wait on an input buffer handle), but I still have no idea how to proceed. A poke in the right direction would be hugely appreciated! =\
~Jonathan

Twisol

Twisol

 

AdLib

EDIT: Abandoned. See next post for details.



As of today I'm working on an engine I call AdLib. It's really mostly just a hobby project... but I have plans for it. Unfortunately, it's on the same backburner as the Atom~View project (tentative name there). I have so many projects concurrently it's not even funny. *sigh*

I don't want to say much about AdLib, not because I'm worried about someone stealing it, but because I don't want to talk about something I'm unsure about. Suffice to say it's a parser.. that and the name should start the wheels turning, eh? Also, at first glance it's almost reinventing the wheel, and I'd rather not reveal too much until I can show why it isn't reinventing the wheel.

Well... I can't resist. Okay, three tidbits and that's all.

1) The engine's name is AdLib.
2) The engine is a parser.
3) (new!) I want to eventually write a module for Apache to interface with AdLib.

Eh, #3 is rather far off right now. Still, it's a goal I want to reach.


Anyone intrigued? Have any guesses? Drop me a comment! [wink]
~Jonathan

Twisol

Twisol

 

Keyboard input

With the help of some of the folks on IRC, I finally got some basic user input working. And it doesn't hog the CPU, either! I think the code speaks better than I can this time.


bool Game::Update(INPUT_RECORD* records, DWORD numEvents)
{
bool retval = false;
for (DWORD i = 0; i {
if (records.EventType == KEY_EVENT)
{
if (records.Event.KeyEvent.bKeyDown)
{
switch (records.Event.KeyEvent.wVirtualKeyCode)
{
case VK_UP:
retval = true; y -= 1; break;
case VK_DOWN:
retval = true; y += 1; break;
case VK_LEFT:
retval = true; x -= 1; break;
case VK_RIGHT:
retval = true; x += 1; break;
}
}

keypress[records.Event.KeyEvent.wVirtualKeyCode] = (records.Event.KeyEvent.bKeyDown != 0);
}
}
return retval;
}

int Game::Run()
{
Draw();

INPUT_RECORD* records = new INPUT_RECORD[1];
DWORD numEvents = 1;
bool redraw = false;

while (ReadConsoleInput(hIn, records, numEvents, &numEvents))
{
redraw = Update(records, numEvents);

if (keypress[VK_ESCAPE])
break;

if (redraw)
{
Draw();
redraw = false;
}

GetNumberOfConsoleInputEvents(hIn, &numEvents);
if (numEvents)
{
delete[] records;
records = new INPUT_RECORD[numEvents];
}
}
delete[] records;

return 0;
}



It's very basic right now, but most of that is going to go into that Keyboard class I mentioned (currently not being used). While I was working this out, I was using a while (true) loop. That turned out to be a very bad idea, because when I opened up the Task Manager, Cripes was sucking up almost 20% of my CPU time. I figured this version out, luckily, which uses far less CPU time because ReadConsoleInput() blocks when there's no input in the buffer.

This code will definitely be changing, mostly as I move code into the Keyboard class and start utilizing it in Run(). For now I'm just glad I can kinda-sorta move the map around. :P

~Jonathan

Twisol

Twisol

 

Cripes 2.0: Entities are working!

At least, they're drawing properly. I got fed up with my code chopping off bits of our poor Sniper, so I went back into the code for Cripes 1.0 - which I dumped in favour of taking a different approach on the code - and got the entity drawing algorithm I had there. I am very, very glad my younger self solved this problem for me already! What my younger self didn't solve is collision detection, which may prove difficult in Cripes 2.0, as well, since the map and the entities are logically separated.

Oh, but then an idea strikes me! (Or was it just a ">boot to the head?) Maybe I can implement the walls themselves as entities. Hmm! That idea needs to be slept on, but I like how it sounds.

EDIT: Oh, -AND- my primitive multiple-frames-per-Entity animation setup works. It's incredibly primitive - just multiple frames and a current_frame indicator - but it does work. It needs some fixing to make it work the way I want it to, though. But it works! ^_^

Twisol

Twisol

 

USC, SAT, WTF OMG BBQ!

My family and I did a Meet USC program yesterday. USC is the University of Southern California, and their Meet USC program is basically a three-hour tour, where the last hour includes talking with the admissions person at a specific school within the university. A little backstory before I go further:

For the longest time, I've wanted to go to Digipen. Sure, no problem, right? Well, a few weeks ago I stumbled across an article on Josh Petrie's blog - yes, that jpetrie - that weighed trade schools like Digipen against traditional universities. A whole lot of what he said made sense to me, and he had even gone to Digipen himself. So going along with the article, I decided to see what my options were.

Ironically - or perhaps poetically just? - the first university I saw was USC. I read up on it and it really looked like just what I wanted. I even managed to talk with an alumnus - on #gamedev no less! - and got his opinion on it. Better, they have a Computer Science degree with an emphasis on Games. It's still a CS degree, meaning I'm not really going for one of those game development degrees so many people have told me to stay away from... like Digipen's. Now, that is irony.

Fast forward to yesterday's Meet USC tour (which I signed up for about a week or so beforehand). It started with a presentation by two peple, one who was going to be a senior next semester and was double majoring, did overseas study, et cetera. The other was an admissions person for the university. They said some stuff about USC, went over applications, and other stuff. Did you know they have over 600 clubs at USC? Yeah. One of them was founded by Will Ferrel while he was there; it's called the Lizard Lovers. The admissions person also told us - oh, did I mention, we weren't the only ones at the Meet USC tour? :P - about an applicant who, when answering an "Explain yourself in three words" question, wrote "A sexy beast".

For the second hour we walked around the campus with a goofy, but nice and informative tour guide. Not much I remember about that hour, although that's when I learned the bit about Will Ferrel's club.

During the third hour, the group got split up according to the studies they wanted to do. We went with the Architecture and Engineering group because the Viterbi School of Engineering is the one that runs the Computer Science (Games) program. A guy who worked there sat down with us (only four of the original group were interested in Engineering) and talked with us about how things would work relevant to the Engineering classes. A lot of good information all around, but by then I was getting hungry. ;P

I absolutely enjoyed the Meet USC tour, and I've definitely decided that this is the university I want to go to. Yaay. :D

Now on to the next acronym. I have to take my SAT and some SAT Subject tests in order to apply. I already downloaded and did some of the SAT Practice Test, but I'm not sure I'm absolutely prepared for the actual test. But I have until October before the next SAT will be held, which is good. I bought an SAT study book from those Kaplan study book people today, and I'm having it shipped in 3-4 business days, so it should be here relatively soon.


Now, after my rambling about college and testing, I'm sure you want to hear more about Cripes. Right? Right? No? Too bad. I've made some changes to how I catch and handle input events, so I can handle mouse and keyboard events simultaneously. It's slightly reminiscient of the typical windows message loop with GetMessage/PeekMessage, which is, in my opinion, a Good Thing.

I'm also slightly surprised at how easy it was to set up simple HUD and GameView windows in the console, but that's kind of the point, anyways. I'll be using the HUD to display the characters and colors you can click to select. I might even make a small menubar at the top to easily load and save maps, and other things like that.


Alright, have fun reading all of that. And as always, comment nao!!

Twisol

Twisol

 

Commitment issues (no duh)

If you haven't noticed, I seem to have some issues committing myself to any particular goal. I know plenty of you have had the same problem with projects, past or present, but it seems to be really ingrained with me. And unfortunately I'm not sure what to do about it, because it's heavily tied into my over-achieving nature. And I -like- everything I do, the problem is the jack-of-all-trades-master-of-none thing. Bleh.

Well, I've finally gotten back to Cripes, starting from scratch. Yes, I'm icing the MUD. It was more than I expected... plus VMware with Ubuntu wrecked havoc on my internet connection. Anyways, I'm putting effort into the game view first, because I know I can do the HUD just fine. That was about the only part I actually finished the first time around, although I want to run over it again later on. We'll see how it goes. Here's a list of my previously finished projects though, just because.

Finished projects:
-Console color/position manipulators
-cConsole (it's a beast, but it's reasonably done)

Unfinished projects:
-"Link's Grand Quest" - my first program ever, and according to my critics, my best work. *groan*
-Arenamatic
-eMUD
-Cripes (in progress)


~Jonathan

Twisol

Twisol

 

On the Subject of Musicshake

I popped onto GameDev.net today and spotted John Hattan's review of Musicshake, a free-to-use music composition tool. It's rather reminiscent of FL Studio, but I never could figure that one out... mostly because none of the instruments seemed to fit. [lol] Musicshake is a lot simpler though, and John compared it to a Strumstick in easiness. I really have to agree. I downloaded Musicshake and it got me started with a randomly created tune from a genre of my choice; I picked Techno/Electronic. I played around with the interface a bit to get used to it, and it's really simple.

To test the waters as I went, I picked instruments that sounded good and messed with the pattern grid and chord selection. It's a bit of trial and error when selecting instruments, but the selection box helpfully adds tags to the left of the instrument names showing what type they are, i.e. [Melody], [Chord Harmony], or any of the others out there. I didn't have to worry about writing a melody, because Musicshake's chord selector for each bar generates one for you. The chords look like wavy lines above the columns, and one would assume that depending on how high or low the line is at a given point in the bar, you get a certain note. But I don't know terribly much about how this thing was made...

The end result was two [Melody]s, two [Chord Harmony]s, and two [Rhythm]s (one with a reverb/normalize tone) combining into a really rather nice and atmospheric tune... so I dubbed it "Atmosphere". Click Here to take a listen.







Also, how do you get the blasted image to center itself? align="center" and style="float:center" don't work. [oh]

EDIT: Thanks, Gaiiden. [grin]

Twisol

Twisol

 

Brief Interlude

I'm taking a Beginning C++ class at my community college right now. It's very basic, no classes or objects yet, even - that's for the next class - but it sort of helps when you have to do certain things. I'm definitely learning some minute details.

With my experience beforehand, though, I'm acing this class handily. Woohoo!

No further progress with eMUD yet, I'm giving it a day to rest. Though here's a free plug to Achaea, the MUD I play all the time. Check it out if you want to get an idea of a high-quality MUD. eMUD will take a lot of ideas from Achaea, mostly because it's such an awesome system, and also because I don't want to have to get used to something else unless it's superior.

~Jonathan

Twisol

Twisol

 

Cripes 2.0 Class Layout

Last updated: 4:49pm on February 17th, 2009

It occured to me while I was writing my last post that even though I might know what these classes do, if I just toss the names out there carelessly the post might take a little work to decipher. So, for my reference and others', here's a class layout for Cripes 2.0 as of now. I'm going to update this post when I add new classes, so don't count on this post staying the same.

Heirarchy:
Game
-Keyboard
-World
--Map
--EntityMap
-EntityFactory

"Independent" classes (granular classes possibly used in multiple places)
Entity
MazeGen


Class: Game
Purpose: This is where my game loop is. I'm not actually sure what Game's exact purpose is. I just think of it as a pseudo-main()-plus-globals thing. This is because my main.cpp is just a main() with a constructed Game and a call to Game::Run(). It remains to be seen whether that's a good idea. (I like to think it is)

Class: World
Purpose: Patches together the components of the visual world, including the world map and its entities. Can be Draw()n, which gathers the clips of the components and copies them together, drawing to a given console buffer (or STD_OUTPUT_HANDLE if none is supplied).

Class: Keyboard
Purpose: Maintains an internal state map of the keys on a keyboard. Its Update() function should be called once per frame. Every key has four possible states, numbered 0 to 3: inactive, down, held, and releasing. When a key is hit, it enters the down state on the next Update(). If it's still down during the next Update(), it's promoted to held. If a key is down when it's released, it's moved to releasing, then inactive. This is to give the program a chance to handle the key before it's released, as it only goes into releasing if it was tapped and released within the same frame/update. If a key is held when it's released, it's moved directly to inactive.

Class: Map
Purpose: Constructs and maintains a CHAR_INFO buffer representing the game map. It can be Clip()ped, trimming the viewing clip given a viewport width and height and returning the result. Some fun cartesian/geometric math here which 'sews' the opposite edges of the map together.

Class: EntityMap
Purpose: Keeps track of an Entity list, and can create a Clip() just like the Map can. There's some -really- fun code involved in the clipping. Yes, maybe even more fun than the Map code! *shudder*

Class: Entity
Purpose: Contains a vector of CHAR_INFO sprites (all of the same dimensions) that can be used as frames for animated sprites. Also has coordinate variables which the EntityMap uses to determine where the entity is on the map.

Class: EntityFactory
Purpose: Factory class that can load Entity definitions from a file and store them in a std::map with string identifier keys. If there's a Player defined in the file, calling ConstructEntity("Player") returns an instance of an Entity with the defined Player frames and size.

Class: MazeGen
Purpose: Constructed with an EntityFactory pointer, it Generate()s a vector of wall Entities given a maze width/height (in cells, not pixels). See this page for the algorithm I use to generate a maze.

Twisol

Twisol

 

More World drawing

First things first: [insert paragraph of rage]. I just lost everything I typed out for this post because I lost my internet connection right when I hit Reply. ARGH.

*sigh* Okay... After some comments and discussion on #gamedev, I decided to get rid of World::View and the DrawView functions. I replaced them with two functions, SetScreenBuffer() and Draw(). The first sets the console screen buffer to draw to (via a HANDLE), and the second draws the World to the screen. Draw() is important here; I'm sure you can figure out in about five seconds how SetScreenBuffer works.

World::Draw() takes two parameters: a SMALL_RECT& defining the viewing rectangle, and a COORD specifying the upper-left coordinate of the view, relative to the screen buffer being drawn to. Being able to specify the screen buffer via SetScreenBuffer() can be useful in case you want to do double-buffering or something, but in this case it's useful because I created a new buffer for the console, and I want World to draw there instead of the original buffer. (Hey, look at that, the last few lines in the editor all begin with "buffer" at the beginning. Including this one! ^_^)

EDIT: I was going to add something into this past paragraph, but I didn't want to break the line of 'buffer' >_>. You can use an offscreen buffer to edit what the View gives you, too. For example, changing the background color or odd things like that. You could draw entities in at this stage too, sprites and whatnot, but in this case, I'm going to let the World handle that.
--END EDIT

Here's the code for Game::Draw(), which calls World::Draw(). It includes the older version of the code too, for comparison.

void Game::Draw()
{
/*/
/// Old version
World::View worldview = world.CropView();
SMALL_RECT drawRect = {0, 3, worldview.Width()-1, worldview.Height()+2};
COORD origin = {0, 0};
COORD worldsize = {worldview.Width(), worldview.Height()};

WriteConsoleOutput(hOut, worldview.CharInfo(), worldsize, origin, &drawRect);
/*/
/// New version
SMALL_RECT cliprect = {0, 0, world.Width(), world.Height()};
COORD drawloc = {0, 3};
world.Draw(cliprect, drawloc);
//*/
}

The first thing I noticed was that it's much easier to specify where to draw, not to mention to see where the drawing coordinate are in the first place. The old version had them integrated into the drawRect. In the new version, everything looks much clearer, and concise too.

Incidentally, this approach makes it a bit easier to add a flag to World::Draw for wraparound views. That is, if the right edge of the map is visible near the middle of the viewing rect, the left edge adjoins to it and continues to the right, wrapping around like a cylinder or sphere. (You don't sail off the edge of a cartographical map, you return to the opposite edge)

Also note the interesting assortment of comment blocks here. Each of the comments with a * in it acts as part of a toggling structure, with the first /*/ acting as the switch. If the switch has two stars (or zero), the old version code is active. If it has only one, the new version code activates. I thought it was nifty.


~Jonathan

P.S. This post is waiting for me to send it right now, because the internet is still down. Thankfully, I saved it to a text file this time, just in case.

Twisol

Twisol

 

Twitter, and Cripes 2.0: World change

I caved in and signed up on Twitter. [rolleyes] I don't know how much I'll use it, but I like how EasilyConfused uses his, so... I also downloaded Spaz to use to update myself. Maybe it'll be easier to keep this going that way. [lol] My twitter link is http://www.twitter.com/twisol, if anyone wants to follow it.

I also mucked with World like I said I might last time, cutting out the Map class and merging EntityMap into World. It definitely feels a bit cleaner. It makes it easier to provide my Strategies with a context, too, so the Entity can test for collision against another entity. I'm still working on how that'll happen, though. Still, things feel like they're shaping up.

Ideally, I don't want to have anything worry about what kind of Entity an Entity is (i.e. Bullet) except the Entity itself and its Strategy. Entity already inherits from IDrawable, so World knows it can draw it (and doesn't need to know anything else). Like I said, I'm working out how exactly the whole Strategy thing will work, but in the end, Entities will really just be treated as Entities or IDrawables, and nothing else.

~Jonathan

Twisol

Twisol

 

Keyboard and USC

Cripes!, the Snipes clone
I've been hard at work on the Keyboard class for the past few days. It's taken me longer than I thought to wrap my head around how to approach interactive keyboard input, but I'm getting there. The Keyboard class now has an Update() method to scan for keyboard events and toggle its internal boolean array accordingly; a WaitForKeypress() method that blocks while waiting for a keydown event; and a Pressed() method that returns the number of keys currently down. The Pressed() method is mainly to help work around the fact that if one key is held down, then another key is pressed and released, the initial key stops sending repeating keydown messages. It does, however, send a message when it is released.

So far it works pretty well, the only problem being that holding the arrow key down causes the world to scroll blazingly fast. I'm pretty sure that's just a problem with how I'm responding to input, not with the Keyboard class itself. Browsing through MSDN while I type this reveals that a waitable timer combined with WaitForMultipleObjects() may solve that, though it's just an idea for now.


College Application - USC
About four hours ago, I finally finished my application to the University of Southern California. The deadline is December 1st.. that'd be today. Cutting it a bit close, eh? Here's hoping I get in! :)

~Jonathan

Twisol

Twisol

 

Snipes 2.0: Maze generation design and code

First: Happy Valentines Day!

Second: I spent my free time over the past couple days trying to speed up my maze generator. I failed, and I sort of understand why, but I'm still a little confused. When I started, I noticed that when I was randomly selecting a wall to break down, if the wall I was breaking down had a visited room on the other side, I grabbed another number ad nauseum until the next wall led to a new room. I figured that the wasteful looping could be optimized out. Well, my "solutions" inevitably involved generating a list of "good" walls each iteration, or removing the entries for the "bad" walls from the surrounding rooms. Not only did it actually slow down the generation (by about 100+ ticks, according to a GetTickCount() delta), most of the time it didn't even work! So, I'm just going to leave it, no matter how much I want to speed it up. At least it works.

Well, that said, I do want to explain how it works. Maybe one of you will see something that can be sped up that I missed. Here's the source code for MazeGen::Generate(), with comments explaining what I'm doing.


struct cell_info
{
cell_info* next[4];
cell_info* last_visited;
bool visited : 1;
bool top_wall : 1;
bool left_wall : 1;
};

std::vector MazeGen::Generate(unsigned width, unsigned height) const
{
// Wall #s
// 0
// 1 2
// 3

// Allocate the cell grid.
cell_info* cells = new cell_info[width*height];

// Initialize each cell.
for (unsigned y = 0; y {
for (unsigned x = 0; x {
unsigned i = x+y*width;

cells.last_visited = NULL;
cells.visited = false;
cells.top_wall = true;
cells.left_wall = true;

// I could probably make do with using the ModFloor function and
// index arithmetic everywhere, and drop the 'next' array, but
// I hate index arithmetic. May as well get it over with here.

// Also: ModFloor aligns a value into a specific range, a bit
// like how integer overflow loops back around to the other end.
// It's how I sew the edges of my Map together.
cells.next[0] = &cells[x+ModFloor(y-1, 0, height)*width];
cells.next[1] = &cells[ModFloor(x-1, 0, width)+y*width];
cells.next[2] = &cells[ModFloor(x+1, 0, width)+y*width];
cells.next[3] = &cells[x+ModFloor(y+1, 0, height)*width];
}
}

cell_info* current = &cells[0];
do
{
// No matter what, we've visited this cell and don't
// want to return.
current->visited = true;

// Are we in a dead-end?
if (current->next[0]->visited && current->next[1]->visited &&
current->next[2]->visited && current->next[3]->visited)
{
// If yes, we need to backtrack.
current = current->last_visited;
continue;
}

// This right here is what annoys me. I feel like it's
// useless cycling, and a waste. But everything I try
// to replace it either doesn't work or is slower.
unsigned which_wall;
do
which_wall = rand()%4;
while (current->next[which_wall]->visited);

// This, at least, is straightforward.
switch (which_wall)
{
case 0: // top
current->top_wall = false;
break;
case 1: // left
current->left_wall = false;
break;
case 2: // right
current->next[which_wall]->left_wall = false;
break;
case 3: // bottom
current->next[which_wall]->top_wall = false;
break;
}

// Before we move, tell the next room that we're coming
// from this one.
current->next[which_wall]->last_visited = current;
// Now move.
current = current->next[which_wall];

// Only true when we've backtracked to the beginning
// with nowhere else to go!
} while (current->last_visited != NULL);

// This code knocks out walls randomly. I kind of want
// to incorporate it into the above somehow, but it's
// simpler and cleaner to leave it here, I think.
for (int i = 0; i 5+25; ++i)
{
// Knock out between 25 and 30 walls.

unsigned which_wall = rand()%2;
unsigned index = rand()%(width*height); // I hate index arithmetic.
if (which_wall == 0) // top
{
if (cells[index].top_wall)
cells[index].top_wall = false;
else
--i;
}
else // left
{
if (cells[index].left_wall)
cells[index].left_wall = false;
else
--i;
}
}

// This is where I create and position each of the walls.
// It's also the only place in all of Cripes where I use Boost...
std::vector walls;
for (unsigned y = 0; y {
for (unsigned x = 0; x {
unsigned cornerflag = 0; // Bitwise mask identifying a corner.
unsigned i = x+y*width; // Index arithmetic. Hate.

if (cells.left_wall)
{
Entity wall = loader->Construct("VertWall");
wall.X = x*8; // I'll get around to un-hardcoding
wall.Y = 1+y*6; // these later.
walls.push_back(wall);

cornerflag += 8;
}
if (cells.top_wall)
{
Entity wall = loader->Construct("HorizWall");
wall.X = 1+x*8;
wall.Y = y*6;
walls.push_back(wall);

cornerflag += 2;
}

if (cells.next[0]->left_wall)
cornerflag += 4;
if (cells.next[1]->top_wall)
cornerflag += 1;

if (cornerflag != 0)
{
std::string cornerID = "Corner";
// Corners! This is the only place I use Boost.
// lexical_cast is great.
cornerID += boost::lexical_cast(cornerflag);

Entity corner = loader->Construct(cornerID);
corner.X = x*8;
corner.Y = y*6;
walls.push_back(corner);
}
}
}

delete[] cells; // I actually forgot this before I wrote this post!
return walls; // I have a suspicion that returning the entire vector
// is a little slow, especially when I'm also passing it
// to the EntityMap (by reference!), but still...
}



Twisol

Twisol

 

Cripes 2.0: EntityLoader

Progress! I've created an EntityLoader class which can load entity definitions from a file with LoadFile(), and return a ready-made Entity to calling code with ConstructEntity(). To try it out, I hand-wrote a Player entity definition into a file and had EntityLoader load it, then I passed the result of loader.ConstructEntity("Player") to the entity map's AddEntity method. Works great! ^_^

EDIT: Class layout updated. Also, I added a link to it at the top of the journal, by the color-coded project name.

Twisol

Twisol

 

Console buffers and learner's permits

Cripes! - The Snipes clone
After doing a little more reasearch, I tried using CreateConsoleScreenBuffer() to create a separate buffer for Cripes to draw to. It turns out that separate screen buffers keep separate state, so I don't really need to worry about saving the old state. My Game class constructor handles the set-up of the console window, and here's the source code:


Game::Game()
: hOut(CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL)),
hIn(GetStdHandle(STD_INPUT_HANDLE)),
hPrev(GetStdHandle(STD_OUTPUT_HANDLE)),
world(40, 25)
{
if (hOut == INVALID_HANDLE_VALUE)
throw std::exception("Unable to create new console buffer.");
else if (hIn == INVALID_HANDLE_VALUE)
throw std::exception("Unable to access console input buffer.");

if (!SetConsoleActiveScreenBuffer(hOut))
throw std::exception("Unable to set the active console buffer.");

SMALL_RECT win_size = {0, 0, 39, 24};
SetConsoleWindowInfo(hOut, true, &win_size);
COORD buff_size = {40, 25};
SetConsoleScreenBufferSize(hOut, buff_size);

SetConsoleTitle("Cripes 2.0 - A Snipes Clone");

CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hOut, &cci);
cci.bVisible = false;
SetConsoleCursorInfo(hOut, &cci);
}








It's actually more simple than I expected. The initializer list sets up the handles and gives our World its dimensions, and the block of if-statements makes sure they're all valid. The next block of code sets the window size, then the buffer size. You actually have to do it in that order here, otherwise we'd set a buffer smaller than the window, which Win32 doesn't like. Then we set the title, and I make the cursor invisible. Take a look:



It actually looks like a normal window filled with a solid blue color. Or it would if the icon didn't scream "CONSOLE!!" I made it that color via a world.FillColor() function I'm using for testing purposes only, so I can see if this stuff is working properly. It is.


Day-to-day Life
I just got my driver's permit. The written test I took allowed a maximum of eight mistakes. I made seven. At least I passed. I'm just a little nervous about driving for my first time... make that really nervous. :|

~Jonathan


EDIT: I don't know what happened here, but it sure looks interesting!

EDIT 2: Ah, I botched my conversion from 2D coordinates to 1D indices.

Twisol

Twisol

 

Cripes 2.0: Maze generation (to-do)

Alright, I'm getting tired of running around on a field of color that makes my eyes bleed. I think Cripes 2.0 is far enough along that I can implement a map generator relatively easily, so I'm going to do that before I go on. It'll make testing collision detection (which I'll implement after this) a bit easier too. Well, to lengthen this post out a bit I'll post my ideas on how I'm going to do this.

The map is going to be generated like a typical bounded 2D maze, except I'm going to open the outer walls up for passage-carving as well, so it'll be able to loop around like it's supposed to. Once every cell has been "touched" I'll run through and remove a certain amount of walls randomly. After that, I'll pass over the result and create wall Entities for each cell, and place them accordingly.

The internal format I'll use to generate the map will be a 2D array of ints, each starting with a value of 3. I'm making each cell responsible for its left and top wall, which lets the cells tile without any extra information or overlap. 0 means no walls, 1 means left wall, and 2 means top wall. Obviously, 3 means both walls. As the generation algorithm progresses, it'll subtract a number from each cell to represent the carving out of the corridors. And after it's done, it shouldn't be very hard to ManufactureEntity("HorizWall"), ManufactureEntity("VertWall"), and ManufactureEntity("CornerWall") (for where walls meet).

Unlike most of my other plans, I put a fair amount of thought into this, and I really like how it sounds. The idea I had before about making the walls Entities, I really like that one. :) We'll see how it goes!

~Jonathan

Twisol

Twisol

 

Cripes 2.0: Maze Generation DONE!

I'm practically dancing in my seat right now! :D I just finished the maze generation. I basically have a MapLoader class with a pure virtual function called Generate(), which takes a width and a height, and returns a vector of Entities (presumably the walls). I derived a MazeGen class from it, which implements a maze-building algorithm and then creates and positions wall and corner entities based on the algorithm results. I haven't added in any code to randomly remove walls, so it's a basic maze right now. Still, I just had to take a screenshot!



Wooooooooooooooooooooo. :D

It takes over a full second to load each maze, unfortunately, because I have so many corner entities. I'm hoping that my random-wall-removal will help mitigate that, but we'll see.

EDIT: Matched the cell sizes to the original Snipes. Each is 8 chars wide, 6 chars high. I wasn't expecting the discrepancy... Also, made my walls blue. So so so much better. It looks almost real! Now for collision detection... well, tomorrow. *yawn*

Twisol

Twisol

 

Cripes 2.0: EntityMap

Just a little update on the Cripes 2.0 design. I changed the EntityManager class into an EntityMap class - a cosmetic change - so I could rationalize giving it a width and height, for the reasons I mentioned before. With that done, I created a Clip() function like the Map class's, and moved the code I previously had in the World for mapping adding the entities to the EntityMap. Much better! Just get the clips, copy the entity clip onto the map clip, and voila.

Slight problem (but very easily fixed): Copying the entity map directly onto the map clip overwrites the map clip entirely. This is bad. Luckily, I thought ahead and decided that '\0' signified "air", as opposed to ' ', which looks the same but is still physically "there". Perfect solution: only copy an entity map block if the character isn't '\0'. Of course, now I'm wanting for a way to designate a clear foreground/background color, but I really don't need it. It would be cool... but I just don't need it, and it's less trivial than this. Oh well! ^_^

EDIT: Yes, this means I can have sprites with holes in them through which you can see the map. Whole lot of good it does me when the Snipes world is just black with blue walls, eh?

EDIT 2: Class layout updated!

Twisol

Twisol

Sign in to follow this  
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!