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

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

 

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

 

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

 

Cripes 2.0: Easing off the hiatus

I'm pretty much done with my hiatus from Cripes 2.0, and I have just one problem... I'm considering changing my Keyboard into a Singleton. But that's stupid, right? There's so many reasons I shouldn't... but it's so tempting. And it's not like I gain much; it's just a design thing, since having two Keyboards would clobber the event queue and render each-other useless... Mrrgh. :|


The Entity subsystem is still a work in progress, though I'm fairly certain I'll have some kind of EntityManager facade. It might not even be a facade, it might actually be the main point of access to the Entity system. This requires more thought. I might be over-thinking this, but I want to get it right. =|

Also: Ubuntu hates me, it really does. I have a partition on my laptop set up for Ubuntu... and after spending maybe five minutes on it, it freezes, and my caps lock light starts blinking. [sad]

~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

 

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

 

An idea which I can't really do much with

(EDIT: Gaiiden, I hate to impose, but if I get on the weekend reading list this week, I'd appreciate if this post was used instead of anything else, since I doubt anything else this week will be very interesting. [lol])

UPDATE: I've decided to start studying basic 3D graphics, and see if I can't make a demo of this concept. If nothing else, it'll be good to see if it's plausible (both as a project and as far as my abilities go).


...so I'm posting it here because I think it's rather intriguing. I was thinking - a terribly dangerous thing to do at the best of times - about objects and functions and such, and how they're basically they're own little units in a codebase. And I was thinking about UML and how it expresses relationships between classes. And then I was thinking about my failed attempt at trying to replicate that... in Notepad. [lol] But the relationships between those units of code intrigued me... and then I began thinking of atoms. Ohsnap, lightbulb anyone?

Atoms are composed of electrons, neutrons, and protons... lets not go into quarks. Depending on the arrangements and numbers of these particles, you get different atoms with different properties. What's more, these atoms can bond with other atoms, creating complex molecules. And these molecules can interact with other molecules in a myriad of ways.
(Can you tell that I'm really getting into this analogy?)

Alright, point being, I believe that one could represent code as particles. And something I would really, really like to see - and perhaps be a part of its development - is a software imager that parses .obj files from a project and creates a 3D scene visualizing the relationships between everything. A class would be represented up close (zoomed in, selected, whichever you like) as an atom, with the nucleus as its member variables, and the electrons as orbiting functions. Further out the class would simply be a sphere, its radius defined by how many variables and functions it contains.

Obviously you can draw many parallels between code units and atomic constructs... which is the point. Not only would it be really, really cool to see how your program looks as an atomic construct, it'd be a highly visual way to see where code is too coupled, where you have linker errors (represented by a connection highlighted in some obviously erroneous way), et cetera. Perhaps it would be integrated into an IDE's toolset.

You could think of it as a highly descriptive descendant of UML. And I'd start working on this project myself, except that I have absolutely zero experience with 3D graphics. And that's a pity, because someone will probably read this and go "I can do that!" and I'd wake up one day to find out someone's started a company called Solar Twilight Enterprises, selling a 3D atomic codeview application. [wink]

Well... if any of you are interested in this, please drop me a comment or PM. Maybe I could get someone who knows the 3D-representational side of things. [smile]
~Jonathan

Twisol

Twisol

 

Verrrry short hiatus from Cripes! 2.0

I have a few non-hobby projects I need to devote my time to, so I'm going to focus on those and get them out of the way. Consequently, Cripes! is going to be a little sluggish for the next week or so.

Books
I got three new books today! No, the GameDev.net book will get here Thursday, but I went to Borders and got some really good programming books. The first, Core Web Application Development with PHP and MySQL, is really, really in depth, covering a variety of pertinent subjects such as classes in PHP (which I've been interested in) and security. I looked at pretty much every PHP book at the store, and this book - though I've only read one chapter right now - looked a lot better than the rest.

The next book I got was Head First: Design Patterns. I wanted a book on design patterns, and I like the Head First series... though it helped that there weren't any other design pattern books, heh. The third book is Sams Teach Yourself Ajax, javascript and PHP, which I got for the same reasons I got book number one: web development. I've never, ever done anything with Ajax or javascript, so I don't feel bad getting a more basic volume.

MusicShake
I've done three other tracks using the MusicShake software since I last posted! I can't seem to extend them very far over two minutes, but I kind of like leaving them short, so...
Atmosphere (01:47)
Flight Over the Ocean (02:29) -- Please excuse the corny title!
Escape Velocity (02:03)
Crystal Music Box (01:47)
Enjoy! [smile] And comments wouldn't hurt either. [wink]
~Jonathan

Twisol

Twisol

 

Books, and a side project

1) I just ordered GameDev.net's new "Beginning Game Programming" book from Amazon.com, and I'm expecting it next Thursday. [smile]

2) I'm going to try to make one Musicshake tune a day for the next six days (excluding today, since I already made one). Should be fun!

~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

 

Cripes 2.0: Basic Strategy support

I have a very rough and basic Strategy setup going now. I have an IStrategy template interface which defines a pure virtual Execute(T&). The templating means I'm able, if I so desire (and I rather don't), to create strategies for other objects besides an Entity. The T& is a reference to the object whose strategy it is.

I then created an IUpdateStrategy interface, same template as before, but with a non-virtual overloaded Execute(T&, DWORD frame_delta). It also has a protected DWORD field to store the passed-in frame delta in, allowing the Execute(T&), which all IStrategies require, access it.

Finally, I made a simple Collide strategy, which doesn't actually test for collision just yet. It requires a World& and a Keyboard& to use for strategic context, the latter mainly because I'm testing it with the player sprite and I need to be able to move it myself. Those two are passed in via the constructor.

I created a Collide strategy and passed it the player entity, and moved its update code from my Game::Update() function to the Collide::Execute() function. It works! But I feel bad design on the horizon, and I really need to just sit down and thing about how I can best use the Strategies.

~Jonathan

P.S. johnhattan's ConFusebox 2 is insanely addicting. [lol]

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

 

Cripes 2.0: 2am memo

This post is more a collection of my thoughts than anything, because I'm too tired to write properly. Comment as you like, if there's anything worth commenting on.

1. Noticed while browsing Wikipedia's Design Patterns area that the Entity resource management design I was calling a pimpl might actually be a flyweight. Or they're close enough to not matter.

2. Read up on the Strategy design pattern, ironically the same thing I was suggesting previously for handing Entity behavior (though I mentioned functors, I was thinking of the same thing). I'm going to take that as a good omen.

3. Considering mucking with my drawing setup (World, Map, EntityMap), because I'm not sure it's the best way to go about it. I could toss the entire Map class at the moment, because I'm not really using it at all. I figure any console application that uses a World would be able to use differently-sized-and-sprite'd Entities to represent the static game world, just like I'm doing with my maze walls. Entities don't -have- to do anything, which is one of my reasons why Entity isn't going to be fully abstract. You can always use a generic Entity.

4. There was something I realized during #3, but I forgot it. I'm sure it'll come back to me. [lol]

5. I remembered what #4 was supposed to be about. I'd like to implement some kind of Z-order for drawing my entities, because at the moment it's just based on what order you add them the EntityMap. Luckily, no sprite should ever overlap with another sprite in Cripes 2.0 - they're alll solid and collidable... well, they will be when I implement collision - so that's way, way at the bottom of the list. (It IS on the list, though; otherwise it wouldn't be in this post)

6. This is the largest codebase I've ever worked on. I feel a little cluttered with all of my files, but I know this is a small project in the grand scheme of things. How does everyone else organize the clutter?


I think that's good for tonight... Good night, all.
~Jonathan

Twisol

Twisol

 

Cripes 2.0: Some all-around refactoring.

I decided to create an IDrawable abstract class and have Entity inherit from it, and did some fixing up so that the EntityMap uses IDrawable instead. I had to use pointers instead of letting the EntityMap own the actual Entities, so now I think I'll be making some kind of manager class that will actually own the entities. I also gave the Entity class a virtual Update() method that takes a DWORD frame_delta that it can use to decide if it's time to change its appearance or move or whatnot. The basic Entity doesn't do anything, while the Missile - which, yes, I've successfully made without much effort - will move itself based on what its aim COORD is.

I'm actually kind of torn between using virtual methods or passed-in functors here. One problem is that the entity Update() method doesn't have any context in the world, so it can't check itself for collision as it moves, or wrap itself around the map (because it doesn't know the width and height, so it doesn't know when or where to set its value to the other side). That's one of the reasons I'm starting to lean heavily towards a passed-in functor... I might actually do that, because it's the only solution that sounds sensible.

I'm also considering doing something with my MazeGen along the lines of splitting it into an object that actually comes up with the maze, and one that takes its output and converts it into entities. The latter might be incorporated into the World as another method... perhaps it could take a MapLoader* - MazeGen's parent abstract class - as a parameter. MapLoader's been pretty much unused, actually - just forward-thinking design - but this might be the time to use it. Hmm.


As a short footnote, my Keyboard class design - or more correctly, my keypress management algorithm - is growing on me, and I'm not even using it! I think, with tweaking, it could be used in a normal application too, though at high update-rates you kind of lose the benefits of the three-state: you're checking fast enough that with human reflexes the chances of a tap going unnoticed are slim. In Snipes, though, I've locked it at one frame every 150ms, which is ample time for a tap to be missed.


That's three topics there (plus a footnote): IDrawable, Entity update functors, and MazeGen abstraction/decoupling. Thoughts?

~Jonathan

Twisol

Twisol

 

Cripes 2.0: More on sprite management

Okay, I thought I'd go into more depth on how I started managing my sprite resouurces. I started by picking out the bits of the Entity that correspond to the sprites, and encapsulating them in a new class, SpriteSet. These "bits" are the CHAR_INFO* vector and width/height fields. I then gave my Entity a SpriteSet pointer. (Along this entire process I'm leaving both versions in, so I can compile and test the code as I go. It also makes for easier refactoring.)

Next, I went to my EntityFactory and refactored it to work with SpriteSets instead of Entities. As I did this I realized I was working with the C++ "pimpl" idiom, and I thought it fit quite well. I think this is one of the times when pimpl is a "logical part of a good design", as EasilyConfused pointed out here (but I honestly don't know much about good design at this point!). I also renamed the EntityFactory to a SpriteManager, because it's more fitting the way I have it now. It's more or less unchanged, except that its Construct() member is now Get(), and returns a const SpriteSet pointer instead of an Entity. The Entity now takes a SpriteSet pointer in its constructor, which could be NULL if you don't have a sprite for it to draw. This setup means you can swap sets of sprites out for an entity, to change its appearence dramatically. For example, my Sniper guy might suddenly turn into a Ghost Snipe if I wanted him to!

During this process I cleaned my Entity class up quite nicely, and with a few tweaks I should be able to inherit subclasses like Missile. I think the best part of this pimpl design is that the sprite management, which should stay constant through all of my Entities, doesn't need to be rewritten every time I subclass a new Entity. I also moved my Corner# entities back into a single Corner with each corner sprite in the sprites file. After all this resource-mongering was done, I checked my MazeGen speed and it was down to fifteen ticks on average. That's shocking.

Twisol

Twisol

 

Cripes 2.0: A short note

Holy smokes!! I put in the resource managing stuff, and my maze generation time dropped to under fifty ticks. No needless copying of sprite buffers apparently! Each Entity now holds a const SpriteSet* to its associated set of sprites. The SpriteSets are created only when loading them in from a file; when EntityLoader Construct()s an Entity, it constructs it with a pointer to its SpriteSet. I had no idea it would make that much of a difference! Better, I think I can turn my Corner# entities back into one Corner entity, but I'll save that for tomorrow... er I mean later today.

For your regularly scheduled journal entry, please scroll down one post. Nothing else to see here!

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

 

Cripes 2.0: Entity refactoring continues

Not much to say. Entity refactoring is breaking stuff that I have to fix as I go, and I'm about ready to just drop it and take a break and/or keep tweaking the rest of it.

In other news, something I did along the way sped my maze generator by about 20 ticks. I'm getting consistent readings of 180 to 205 ticks, rather than the 200+ I was getting before. Interesting.

Twisol

Twisol

 

Some small fixes/changes

I'm giving myself some time to think about the next big task to tackle (collision detection... no pun intended!), so today I went through the code and made some minor changes, mostly cleanup. I'm also thinking about how I want to handle missile Entities, like the bullets fired by the player and the snipes. I'm thinking I'll have to refactor the Entity code a bit, but it shouldn't be too bad.

Done-Today List "Pause" on lost focus
To do this, I compare GetTopWindow(NULL) with GetConsoleWindow() in the main loop. If they're not the same (ie. I don't have focus), I call keyboard.Clear() to clear the Keyboard state. I also flush the console input buffer. This is OK because the only messages that come in are keyboard, mouse, and buffer resize events. It also lets me WaitForSingleObject() on the input buffer, so I know exactly when I regain focus.
Renamed EntityLoader to EntityFactory
Well, it is!
Changed my movement code very slightly
Changed from if (keypress[VK_*]) {++y;} y = ModFloor(y, min, max); to y = ModFloor(y+keypress[VK_*], min, max);. Since keypress[VK_*] returns a bool, and a bool converted to an int is 0 or 1, it works nicely and is more compact.

I'm such a slow worker :(

EDIT: Also:
Replaced my CHAR_INFO buffer copy loops with a simple memcpy() call.
Because I wanted to. It seems like it'd be faster to just copy a block of memory at once rather than copy each individual element and its members over the course of a loop, anyways.

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: MazeGen fixes/changes

So far today I've tweaked the MazeGen algorithm a bit, making it remove walls randomly and speeding it up. I still have some optimizations I'd like to try, but it's loading almost immediately now, instead of taking upwards of two seconds.

First, I added a for loop that randomly removes a random amount of walls (between 25 and 30 seems to be perfect). This didn't help much with speed, but it definitely made the maze more fun to run around. Before this change, it was really just one long corridor with the occasional small branch. Now, it's a whole sprawling complex of hallways, junctions, and dead-ends. Awesome.

I also changed how I was storing and handling CornerWalls. On a hunch, I guessed that what was really slowing the generation down was how every CornerWall stored a sprite for every kind of corner wall that could be made. I fixed this by splitting the CornerWall's fifteen sprites into fifteen Corner# entities, each with just one sprite. It sped up immediately! Good thing the other walls (HorizWall and VertWall) only need one frame.

Should I upload a demo so everyone can run around the mazes themselves? It's pretty nifty. ;)

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: 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

Sign in to follow this  
  • Advertisement
×

Important Information

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

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!