hello
Tuesday, February 10, 2015
Friday, October 19, 2012
Reluctant Rogue - HTML5 and leaving C#
I've wanted to get back to the Roguelike game that I spent time working on a couple of years ago. The working title for the game is "Reluctant Rogue". Since then, I have stopped coding for my day job, and have picked up much more JavaScript knowledge. I never wanted to make the game platform dependent, so I decided to start over and rewrite the game in HTML5/JavaScript.
Why HTML5/JavaScript? For one, I really like the way JavaScript and jQuery interact with elements on a page. I recently worked on digitizing a board game that some friends of mine created some time ago (You can find it at here). I wrote it entirely in HTML and JavaScript/jQuery - and it was a very cool experience. Nothing to compile, cross-browser compatible, etc. Making a game in JavaScript also has an awesome "blending of the worlds" between "drawing" and "logic". I don't have to store a separate array of the game pieces, with their locations and rotations. Instead, I use jQuery to move the pieces around on the board. This literally updates their onscreen position, rotation, etc. Want to know where a piece is? Just look it up on the board - The data is all within the pieces themselves! This way of coding flows well with functional programming and is honestly way more fun to write.
RR is a little more complicated though, involving AI, a camera, etc. I don't want to rely on CSS to space out the dungeons, so I decided to use HTML5. The canvas allows you full control of the position of the art elements.
Plus, there are some kickass JavaScript libraries out there, such as processing.js and of course, jQuery!
Finally, I don't know any HTML5. It seems like the perfect time to learn it. I also need to improve my JavaScript knowledge beyond the level of "script kiddie" - making full use of advanced JavaScript functions.
My Visual Studio license also expired recently... totally unrelated!
Why HTML5/JavaScript? For one, I really like the way JavaScript and jQuery interact with elements on a page. I recently worked on digitizing a board game that some friends of mine created some time ago (You can find it at here). I wrote it entirely in HTML and JavaScript/jQuery - and it was a very cool experience. Nothing to compile, cross-browser compatible, etc. Making a game in JavaScript also has an awesome "blending of the worlds" between "drawing" and "logic". I don't have to store a separate array of the game pieces, with their locations and rotations. Instead, I use jQuery to move the pieces around on the board. This literally updates their onscreen position, rotation, etc. Want to know where a piece is? Just look it up on the board - The data is all within the pieces themselves! This way of coding flows well with functional programming and is honestly way more fun to write.
RR is a little more complicated though, involving AI, a camera, etc. I don't want to rely on CSS to space out the dungeons, so I decided to use HTML5. The canvas allows you full control of the position of the art elements.
Plus, there are some kickass JavaScript libraries out there, such as processing.js and of course, jQuery!
Finally, I don't know any HTML5. It seems like the perfect time to learn it. I also need to improve my JavaScript knowledge beyond the level of "script kiddie" - making full use of advanced JavaScript functions.
My Visual Studio license also expired recently... totally unrelated!
Saturday, February 11, 2012
Lessons from using JSIL to convert C# to JavaScript
So I big reason I am still considering using C# is that there is a tool out there to convert C#/XNA games to JavaScript. This seems like the best of both worlds - game can be maintained in readable JS and I can update code in the language I am most comfortable with. However, JSIL is open-source with zero documentation. Recording my lessons learned here:
Getting Started
Assumptions:
- You have Visual Studio 2010 and understand how to make .exe files for your programs
- You have a basic understanding of SCM systems
- You have programmed before, and are somewhat technical
Getting the source code:
- Download Git here. Don't worry - we just need the full source
- On installing, be sure to opt-in for the Command Line client (Windows)
- Open the command line client. create a folder for the project and navigate there (Linux commands 'cd', 'mkdir' etc. work here)
- Clone the JSIL repo recursively, including sub-modules:
git clone --recursive https://github.com/kevingadd/JSIL.git
- Congratulations! You have the source!
Generating the JSIL .exe file
- Open the JSIL.sln solution file
- Rght-click on the solution file in the Solution Explorer
- Select "build all"
- the JSILc.exe file will be placed in the bin directory of the project
Using the .exe file to convert C# to JavaScript
- The JSILc.exe file needs to be run from the bin directory - i.e. you can't move it around (seemingly)
- Open a command window in this directory
- Go back one directory, hold shift and right click the folder. You should see an option to open a command window there
- You will have to run this .exe from the command window.
- Bring your .exe file that you want to convert into the bin directory
- "apply" JSILc.exe to your .exe. In the command prompt, type the following (remove the <> signs):
- JSILc.exe <yourexe.exe> --out=<destination directory>
- Congrats! You have converted source.
This is where I'm stuck - I get errors when I try to run the .js files that I can't resolve:
'JSIL is undefined'
I also don't really know which .js file to "run" - manifest.js? There isn't much help here.
Things to avoid
- Do not assume you can download the source from GitHub - you need to do a full recursive clone using Git to get all the right references for JSIL
- Do not move JSILc.exe around - it seems to fail outside of the build output directory.
Thursday, June 9, 2011
Opening Super Comrade Oriam's casket
He is badly decomposed, but we can rebuild him!
A Redditor recently announced that he developed a C# to JavaScript compiler, and the demo was the tutorial XNA game! This is perfect as JavaScript is cross-platform and easily portable.
In the end, though, if the JavaScript can't work, at least I'll have updated SCO to XNA 4.0, Visual Studio 2010, and Farseer Physics 3.2. There is a great tutorial for converting from Farseer 2.x to 3.2 here.
Monday, April 25, 2011
Regular Expression Search/Replace with IntelliJ
I recently got tasked with reviewing some HTML documentation for my company's product. In the documentation, many instances of the abbreviation "ID" were used. Except, they were being used inconsistently. Sometimes it was "ID", sometimes it was "Id", and sometimes it was "id". Luckily, IntelliJ supports find/replace with regular expressions.
find: id([ .,;!])
replace with: ID$1
The capture group (in the parenthesis) preserves the punctuation if there is any with the "$1" part of the replace.
find: id([ .,;!])
replace with: ID$1
The capture group (in the parenthesis) preserves the punctuation if there is any with the "$1" part of the replace.
Wednesday, April 13, 2011
Sunday, July 18, 2010
Dream Journal
I have had vivid dreams for the last two nights. I used to keep a dream journal and write all my dreams down. They say it helps you remember dreams more easily in the future. I stopped because it was a hassle.
The dream last night started somewhere and had some stuff happen that I don't really remember. One part I was on an island in the sky with others, and we would travel by thrusting ourselves into these long gray dirt tunnels. One of these lead to the Netherlands, and that was one of my companions homelands. This is probably due to a line from Gettysburg, which I watched last night.
I was working for someone who told me to spread around these dangerous, poison capsules. I remember objecting and being told that it didn't matter. There was a lot of small trains on a lot of small tracks around this countryside. There were lanterns on the back hitch of these trains, and I remember a contest to get one. I tried and failed. At one point a train went by and put gravestones down in-between the tracks it was on. apparently, the train could still move over them. The part I remember vividly is an abandoned house. I went into the house alone, and remember it being quite frightening. I then had a companion, though, who I think was a friend of mine named Laura. She told me this is the house she grew up in, and we thought it was haunted. She told me to lay the capsules, which looked like small, wax, orange-syrup filled scimitars, on the table in the dining room. I also had small , round, capsules of butter that I laid around the house. I guess the goal was to get someone to pick them up, one of the haunters.
There was also a small shack across the road from this abandoned house. The house was a three-story house on a hill, will a full basement. We left that place and came back later, and went through the kitchen and the dining room where the table was. The capsules were gone, and instead there were small blue squirt guns in various positions pointing at us. They started firing, but Laura remembered doing this as a child and the way to make them stop was to simply yell "stop!" and point at the gun. We did that and they stopped, and then magically a small girl appeared behind each one and laughed and scurried away.
We then all sat at the dining room table, and then Laura's mother appear with great duress to Laura. she noticed my presence, but not Laura's. She offered me cereal and began talking to me like I was one of her daughter's friends. I remember her telling me she had five daughters and five sons, but the sons never appeared. She seemed to be stuck in a time past. I had a magazine with me, and she commented, "how come I can see your feet through the table?" The magazine was dated 2010. I then realized she could not see anything that was "out of her time," which was about 1996. She seemed troubled by this, and I asked her if she knew anything about mental diseases, or slow degrading of the brain. She said no, but I pointed out the hole in the table which wasn't there, and she started to break down. I told her to follow me. I took her out of the house and across the street, which was now a large, three-story house build around the shack that was once there. Laura's mother told me how her husband lived here, and they were getting separated. I took her to a room upstairs and turned on the light and told her to look around. She seemed to vaguely understand what was happening. I told her to look at the magazine, and asked if she could read the date. "2010..." she said. I told her that that was the current year and time, and she has been stuck in the mental state of fifteen years ago. Slowly she changed and looked around, and fully realized where she was and what was happening. I was very excited to show Laura. I went out into the main living room on the third story of the shack-house. there was a lot of people here, but not Laura. I yelled for Laura, thought I saw her, but it was only someone who looked like her. I then yelled for any Williams sister, and could find none. I panicked and ran back to the mother's room. She was emulating some sort of martial art move that someone was showing her, and she lost her balance and fell on the ground, hitting her head. I then saw a computer diagram that showed an image she had not recognized. There was a date in the corner of this image -- 2010. The image started to disintegrate, starting from the lower right. I yelled "No!" at increasing volumes. The image disappeared and was replaced by one that simply stated, "1996." I was heartbroken, and full of anguish.
The dream ended here, and I awoke with a start. It was a very painful dream, and very interesting to me. I often dream, but the last two nights have been very vivid. They involved doing things under a lot of duress, but were not nightmares. This one in particular is very interesting to me because it contains two things that are not supposed to occur in dreams -- color and being able to turn lights off and on. I don't know what this means or if it is significant. I have had color before in my dreams -- the orange I know very well.
Tuesday, June 15, 2010
How do you think this will end?
I just submitted this question to Rhapsody customer support:
"Long ago, I had an account set up with Musicmatch, using the Musicmatch
desktop application. I purchased quite a few songs for that account, but then I forgot about it for three or four years.
Since Musicmatch was acquired by Yahoo and subsequently Rhapsody, I am afraid my account, and the songs I purchased, have been lost in the shuffle.
I believe the username I was using for the Musicmatch desktop application was "getimoliver".
However, I can't log into Rhapsody using this username. I have also tried using the "forgot your password" link.
Would it be possible to retrieve my old account, or at least the songs I purchased on it? I am able to provide the credit card number I used to purchase the songs, my full name and address, and my old login information.
Thank you!"
I'm not getting my hopes up. Will post the response I receive.
Question Reference #100615-004308
Tuesday, April 13, 2010
Japanese Bowling
The way Japanese people will hold a conversation with each other is very different from the way most Westerners converse. In Japan, a conversation is like a game of bowling. The most respected of the group will bring up a topic, and mention something about it. Then, the next person in line will take a swing at the same subject. Just like we take turns in bowling and do not directly compete.
Western conversation, however, is more akin to a game of tennis. One person will direct the conversation either at one person in particular hoping to elicit a response, or will throw a topic out for the another person to send back.
The Japanese lead a very structured and rules-driven life. It is not surprising that they would hold conversations differently than those of the west. It is also not surprising that the XP practice of Kanban came from the Japanese. Unlike the West who loves to "cowboy code," Kanban presents a very structured and detailed way to accomplish work which (no surprise) works better than the ways of the cowboy.
I think we should consider adopting the Japanese way of conversing for some situations.
I was in a meeting today with another part of our company, discussing integration points. I brought up a very simple question: how detailed do we want an invoice? It took literally 30 minutes to get this question answered. I threw the ball out there to be returned, but instead of it being returned to me it was stolen by my project manager, who went on to play his own game of tennis with other people in the meeting. It was only after no one was playing at all that I had a chance to ask my question again, and stay involved enough to get an answer.
When I go to a meeting I come with a set of a few simple questions that I would like to get answered. Once they are answered, I have no problem casually tossing the ball around to get a better idea of general topics. When I wanted the answers, though, I could not concentrate on what was being said. Instead, I was seeing when I could get the ball back in my possession. There has to be a better way to do meetings.
The military is another place we can see structure. Rank is strictly enforced, but all are given a chance to speak, albeit with respect and with a staunch time limit. The military must be trained to focus and move as a single unit. If they do not, they will fail in their tasks, or at least be much less effective.
Therefore, instead, we try the structured Japanese way of conversing. We all sit in a circle. The highest ranking member brings up a topic, and we all comment on it in turn until there is nothing more to be said about the topic. A time limit of a minute or so is placed on each person. Then the next person in line repeats the process. We do not ping-pong the ball back and forth between two or three people in a meeting of six. Everyone is given equal opportunity to speak, and no one leaves feeling they have unanswered questions. We increase focus and effectiveness.
You may recognize this meeting schedule; It is a derivative of the standup meeting.
Now the only problem is getting people to try it...
Friday, February 26, 2010
Coder Journey
Listening to a random Reddit-linked YouTube clip of a man carving a Big Daddy Alpha Series out of wood. There is a Bioshock medley playing in the background... and it has some grand violin flourishes. You know the ones -- the lone violin reaches some high note while the rest of the strings do that background noise thing they do.
There is one point near the end of the clip when one of those flourishes occurs and it sends shivers through me. This happens to me with this style of violin, and I get a really sad feeling. I decided to meditate over that feeling and ended up having a small journey.
It started out at the base of a hill. There was a small door with a huge wooden statue of the Moose over it. I went in and headed down the red-carpeted steps faster than I usually do. I opened the door revealing a hilly area where the colors did not make any sense. I saw the Moose and started walking toward him but it was one of those illusions where I keep walking and get no closer. Eventually I went into a hole in the ground and slid down into an area that looked like Rapture. There was only one room with a glass wall to the ocean outside. I saw the Moose floating there, and I put my hand on the glass. He put his hand there in return and then slammed his face into the glass and screamed. I stepped back and turned around to see the room had expanded, to a kind of hall. There was an indent in the middle of the room. There was water coming down from a crack in the glass ceiling. It formed into a small stream that defied gravity and went up the small 2 steps leading into the indent. I followed it and it lead me down a tube where the floor was curved and got higher and higher until I was crawling with the water in my face, still following it down. The tube opened up some and I fell, and I saw the tube circle back into the rock that was supporting the first room. I didn't want to leave the tube, or leave Rapture, or maybe I didn't want to go into the rock. But I did, and it lead down some sharp steps. The rock was an inversion of my first meditation plateau. I followed the water to the bottom where it just vanished into nothing, and there was nowhere for me to go. I felt very sad and nervous. Then the Moose started coming down the steps, two or three of them. I got on my knees, my hand touching the floor where the water was disappearing. I saw random images at this point, one of which being a whale and the other being a guardian I had seen before. They were all fleeting. Suddenly, I appeared on a wheat or tall grass filled plain and I saw the girl I usually see when I meditate, who lives across the river. I reached out to her but the same illusion occurred where I walked but got no closer, but in reverse. I just reached and it was like her and her surroundings were pulling away. Then I was back at the bottom of the rock, and the Moose was on top of me, enveloping me and we sank into the Rock. I lost track of myself then.
I think there are two places to go when you meditate. You can expand your presence and see how far up you can go, or you can shrink your presence and see how far down you can go. The journey ended and I felt so incredibly minuscule. But I also felt like I had found something.
I think many of us are afraid to look too deeply inside. Religions seem to all play to the opposite, and go up as high as you can. We should go to both ends when we ponder that which we are trying to understand.
Tuesday, February 23, 2010
Automation
No nice code snippets to show this time, but hey, we have liftoff!
Caveats:
- I'm re-reading the doors every time a tile is rotated or flipped. This isn't what I had envisioned initially, but it works. The problem with my last post was, while it worked, it didn't work with the tile randomizer I envisioned. The automator relies on finding an open door on the floor somewhere and keeping that as the "potential door." The automator then shuffles through all available tiles, and when it finds one with a matching door, proceeds with flipping it and moving it around until it can be placed. If we rotate the tile in this process, we lose track of which door it was selected to fit into the potential door. Re-reading the doors after rotation also loses the original potential door, but finds a new one in it's place. Granted, this could be done with the old method too, but frankly it was easier this way. The old way also didn't work with rectangular tiles.
- Automation is not fully complete, although it is a huge step forward. There are times when I'll end up with open doors and tiles that I know will fit, but the automator has run out of iterations.
To get this far I had to take a step back from the automator I had in place, and essentially re-write it. When the manual placer placed a tile, we have a concept of a "tile position" which is the upper left corner of the tile. The automator had no such luxury. We had to decipher the tile position given only the position of the potential door and the inner position of the selected tile.
There are a few places in the code where I got screwed up with this logic, and it was hard to track down where I messed up until I started over.
My original plan is still what I ended up sticking with! I got caught up with figuring this out and threw out Orientation in favor of Direction, thinking knowing more would help me out. However, how do you determine direction? I thought it was easy, but ended up being not worth it at all.
This is also a good example of where the brain is superior to the CPU. We can look at two jigsaw pieces and see if they fit relatively easily. A jigsaw puzzle is essentially what this automator is. A CPU, though, has no such luxury. Our brains can get caught up in making a seemingly easy task understandable by a purely logical system that can only evaluate one thing at a time.
Bugs I won;t make again:
- I assumed only one door could match a time, and only removed one door at a time when placing.
- I tested with doors that took up all of one side, missing a bug where the vertical/horizontal displacement of a door had to be taken into account.
Wednesday, January 13, 2010
Rotation... Again
Down to finally matching doors, but it took a helluva effort.
I figured out how to rotate an array completely, but given a certain index in the array? This was just as difficult as figuring out how to rotate an array in the first place.
The problem was, I was looking for an easy formula I could plug-in and just go. It seems like, given an index, it should be easy-peasy to find the rotated variety. What I was forgetting, however, was that it's never that easy.
Short of rereading the door positions every time I rotate, I figured I could look up rotating a point around another point:
x' = Cos(Theta) * x - Sin(Theta) * y
y' = Sin(Theta) * x + Cos(Theta) * y
Where Theta is radians from degrees of desired rotation. This showed me, though, my biggest mistake -- it only rotates around the origin. X=0, Y=0. I had to specify the origin as the center of the array. After I came to that realization, it all fell into place:
private void rotate(Vector2 point, Vector2 origin, int degrees)
{
float radians = MathHelper.ToRadians(degrees);
Vector2 v = point - origin;
v = new Vector2((int)Math.Round((v.X * Math.Cos(radians) - v.Y * Math.Sin(radians))),
(int)Math.Round((v.X * Math.Sin(radians) + v.Y * Math.Cos(radians))));
point = v + origin;
}
That big chunk in the middle is the standard rotation formula.
Now, my doors fit as long as the orientation and positions match! Although the positions when rotated are no longer exclusively the upper-left... so I have to work in the door width for the final fitting calculations.
Victory!
Monday, October 19, 2009
The speed of the doors
A lesson I learned from Super Comrade Oriam is that as much as I hate to admit it, runtime speed is a great equalizer among games. SCO was bloated, and probably relied too heavily on it's physics simulator. The game ran well, but only on pretty new machines.
When I found doors in the post below, I used a Tile object. When I wrote the rotating 2D array code, I also included twin methods that rotated an array of ints. Is the int rotation faster? I don't know, but it certainly can't be slower. So I took a look at my findDoors() method on my Tile class, and thoght, "How can I make this work with just integers?"
* * *
C# has support for enums, as any good high-level language does. So instead of worrying about "Blocks" and "Block types" I translated all the info I would need into an enum. Essentially, the enum is the tileType, meaning I don't have to compare strings when I want to figure out Block types.
I had to get rid of the idea of a 'visited' block, or at least, the idea of a boolean that lives on the Block object. That changed the logic of my findDoors() in such a way that I couldn't figure out how to move forward. It was one of those instances where I just had to put the idea on a back burner, and let it simmer.
After about a week, something clicked and I knew I knew the answer, I just had to code it. Instead of checking whether or not a block was 'visited', I simply gave it a new BlockType of 'Visited.' 'Visited' is only given to door blocks that have been visited by one of the Peek() methods. So when looking at a door to figure out the width, it must simply be a BlockType.Door. When Peeking() right or down to determine the width of a door, though, either BockType.Door or BlockType.Visited will count toward the width. This is because Doors can share corners. Also, Since each Block is only looked at once, this also guarantees that I won't accidentally make duplicate doors.
Pretty exciting if I say so myself.
Monday, September 14, 2009
XNA and Drawable Game Components
XNA has this feature of letting your classes inherit from DrawableGameComponent, or GameComponent. What this inheritance does is let you write your own overridden Draw, Update, Initialize, etc. methods that will be executed during the corresponding base.* call in the main game class.
The overridden Draw, however, does not take a spritebatch, or anything else but gameTime, though. So how do you actually draw anything in those methods? The answer is two-fold:
1. Don't make too many classes that actually draw something. Make more 'master classes' that draw everything contained in them, like maps or menus.
2. Game.Services.GetService
"Game.Services.GetService?" you say, "how does that do anything?" To that I say, "ah, it lets you pull generic services defined on the game!" So when your spriteBatch gets initialized in the main game class:
spriteBatch = new SpriteBatch(GraphicsDevice);
you can add it to the services of the game:
Services.AddService(typeof(SpriteBatch), spriteBatch);
and pull this service from any class:
spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
Just remember to declare your own spriteBatch variable in the subclass, and pull it from the game some point after it's been initialized!
The overridden Draw, however, does not take a spritebatch, or anything else but gameTime, though. So how do you actually draw anything in those methods? The answer is two-fold:
1. Don't make too many classes that actually draw something. Make more 'master classes' that draw everything contained in them, like maps or menus.
2. Game.Services.GetService
"Game.Services.GetService?" you say, "how does that do anything?" To that I say, "ah, it lets you pull generic services defined on the game!" So when your spriteBatch gets initialized in the main game class:
spriteBatch = new SpriteBatch(GraphicsDevice);
you can add it to the services of the game:
Services.AddService(typeof(SpriteBatch), spriteBatch);
and pull this service from any class:
spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
Just remember to declare your own spriteBatch variable in the subclass, and pull it from the game some point after it's been initialized!
There are very few articles or threads I could find that dealt with this exact problem. Most ranged from either a really simple drawing that occured in the main game Draw(), or more complex drawing that didn't use a spritebatch, at least not on the top level. I finally found an article here that was simple and consice enough for me to decypher what was going on. Props to the author!
Monday, August 10, 2009
Rotating a 2-D array
Not as easy as you would think:
public static Object[,] rotateClockWise(Object[,] inArray)
{
int width = inArray.GetLength(1);
int height = inArray.GetLength(0);
Object[,] outArray = new Object[width, height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
outArray[i, j] = inArray[height - 1 - j, i];
}
}
return outArray;
}
public static Object[,] rotateCounterClockWise(Object[,] inArray)
{
int width = inArray.GetLength(1);
int height = inArray.GetLength(0);
Object[,] outArray = new Object[width, height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
outArray[i, j] = inArray[j, width - 1 - i];
}
}
return outArray;
}
Monday, July 20, 2009
Doors have been found!
Here is what I ended up using to find all doors in a tile. It could use a little refactoring, but it works!
The trick was the VISITED boolean. I wondered how I could make sure to evaluate doors that were on corners, but I didn't see how to do the evaluation without going through the tile twice -- once looking for vertical doors, and once looking for horizontal doors.
This way, each block is evaluated one-at-a-time. As the width of a door is found, it sets each block to 'visited' status, so it doesn't look for another door. Unless, that is, there is another door block to the right or below the current block. Then, the block is considered not visited, and will still be evaluated when the one-at-a-time evaluation hits.
Think of it as like a going through a maze. If you come to an intersection, you want to consider that intersection 'unexplored' until you have a chance to come back and explore the other paths.
private ListfindDoors()
{
ListfoundDoors = new List ();
for (int i = 0; i < blockGrid.GetLength(0); i++)
{
for (int j = 0; j < blockGrid.GetLength(1); j++)
{
if (blockGrid[i, j].type == "door" && !blockGrid[i, j].visited)
{
foundDoors.AddRange(figureWidth(i, j));
blockGrid[i, j].visited = true;
}
}
}
return foundDoors;
}
//Figures width of a door. Will check both horizontally and vertically.
private ListfigureWidth(int i, int j)
{
Listdoors = new List ();
int width = 1;
Vector2 position = new Vector2(i, j);
width = PeekRight(i, j, width);
if (width > 1)
{
doors.Add(new Door(width, position, Orientation.Horizontal));
}
width = 1;
width = PeekDown(i, j, width);
if (width > 1)
{
doors.Add(new Door(width, position, Orientation.Vertical));
}
if (doors.Count == 0)
{
doors.Add(new Door(1, position, Orientation.None));
}
return doors;
}
private int PeekRight(int i, int j, int width)
{
if (DoorOnRight(i, j))
{
width = PeekRight(i, j + 1, width + 1);
blockGrid[i, j + 1].visited = true;
if (DoorBelow(i, j + 1))
{
blockGrid[i, j + 1].visited = false;
}
}
return width;
}
private int PeekDown(int i, int j, int width)
{
if (DoorBelow(i, j))
{
width = PeekDown(i + 1, j, width + 1);
blockGrid[i + 1, j].visited = true;
if (DoorOnRight(i + 1, j))
{
blockGrid[i + 1, j].visited = false;
}
}
return width;
}
private bool DoorOnRight(int i, int j)
{
bool doorOnRight = false;
if (j < blockGrid.GetLength(1) - 1 && blockGrid[i, j + 1].type == "door")
{
doorOnRight = true;
}
return doorOnRight;
}
private bool DoorBelow(int i, int j)
{
bool doorBelow = false;
if (i < blockGrid.GetLength(0) - 1 && blockGrid[i + 1, j].type == "door")
{
doorBelow = true;
}
return doorBelow;
}
The trick was the VISITED boolean. I wondered how I could make sure to evaluate doors that were on corners, but I didn't see how to do the evaluation without going through the tile twice -- once looking for vertical doors, and once looking for horizontal doors.
This way, each block is evaluated one-at-a-time. As the width of a door is found, it sets each block to 'visited' status, so it doesn't look for another door. Unless, that is, there is another door block to the right or below the current block. Then, the block is considered not visited, and will still be evaluated when the one-at-a-time evaluation hits.
Think of it as like a going through a maze. If you come to an intersection, you want to consider that intersection 'unexplored' until you have a chance to come back and explore the other paths.
Wednesday, July 8, 2009
2-D Arrays
I graduated my Computer Science focus, you would've thought that I would know how to reference a 2-D array. First number is the array number, and the second number is the number within that array. I thought It was like a grid, where a reference took x/y coordinates. Instead, it's y/x. Lesson learned!
Tuesday, June 30, 2009
The Random Dungeon That Isn't So Random pt. 2
A tile can have any number of doors, in any position in the tile -- they shouldn't be limited to the outside edges of a tile. This means that when other tiles are attached, simply having a door of the same width is not sufficient. The algorithm will have to handle:
Doors
Doors are the crux of the dungeon, and there are still a few things about them that will be tricky.
Determining a door's width is straightforward:
However, this algorithm does not say what to do if the door shares a corner with another door...
- There are no tiles that have a door of the same width.
- There are no tiles that could be added that do not overlap an already placed tile.
- Create a random door of a random width in a random location in the dungeon.
- "Finish" the dungeon and place the exits and entrances.
Doors
Doors are the crux of the dungeon, and there are still a few things about them that will be tricky.
Determining a door's width is straightforward:
- Iterate through all the Tile's Blocks until youyou hit a "Door" block.
- Mark this block as "evaluated"
- Evaluate the blocks to the right and under the current block. Find where the door "continues" and count how many door blocks are reached in that direction until a non-door block is hit.
- Do the same evaluation in the opposite direction. This will be the true width. Mark each door block as "evaluated" as they are evaluated.
- When a non-door block is reached, the position of this block will be the door's position within the tile.
- Add the door, with it's position and width, to the Tile's list of Doors.
However, this algorithm does not say what to do if the door shares a corner with another door...
Friday, June 26, 2009
The random dungeon that isn't so random
There is an SNES game I played when I was younger called Breath of Fire II. It was pretty standard RPG fare, except for the bonus dungeon.
Many RPGs have bonus dungeons to explore when you beat the game. Usually, the bonus dungeon has the hardest enemies in the game, and is not related to the story. The bonus dungeon in Breath of Fire II, however, was a Rogue-like dungeon. It was a dungeon that shared qualities with the game "Rogue," namely randomly generated levels with a focus on combat. It turned out to be my favorite part of the game.
Fast forward a few years. I created a Mario Bros. Clone named Super Comrade Oriam that used picture files to create playable levels. I liked this idea, so after I completed Super Comrade Oriam, I decided to create a Rogue-like RPG where random dungeon "tiles" could be easily created by the user with picture files.
That is where the problem lies.
All other random dungeon algorithms I have come accross are completely random. Either the algorithm spawns "rooms" that then get connected randomly with thin hallways, or the tiles to create the dungeon are premade by the designer, so he/she already knows how the tiles could fit together. The best example I found is here, but even this example is too random, and cannot be user defined. I want the user to create their own rooms, as well as hallways, that can lend to an overall style of a particualr set of dungeon levels. So the user can define a large, open dungeon with many columns, not just rooms that randomly sprout off other rooms.
I need to create an algorithm that can read in a tile, and figure out how that tile connects with other tiles. I have decided on some restrictions:
Many RPGs have bonus dungeons to explore when you beat the game. Usually, the bonus dungeon has the hardest enemies in the game, and is not related to the story. The bonus dungeon in Breath of Fire II, however, was a Rogue-like dungeon. It was a dungeon that shared qualities with the game "Rogue," namely randomly generated levels with a focus on combat. It turned out to be my favorite part of the game.
Fast forward a few years. I created a Mario Bros. Clone named Super Comrade Oriam that used picture files to create playable levels. I liked this idea, so after I completed Super Comrade Oriam, I decided to create a Rogue-like RPG where random dungeon "tiles" could be easily created by the user with picture files.
That is where the problem lies.
All other random dungeon algorithms I have come accross are completely random. Either the algorithm spawns "rooms" that then get connected randomly with thin hallways, or the tiles to create the dungeon are premade by the designer, so he/she already knows how the tiles could fit together. The best example I found is here, but even this example is too random, and cannot be user defined. I want the user to create their own rooms, as well as hallways, that can lend to an overall style of a particualr set of dungeon levels. So the user can define a large, open dungeon with many columns, not just rooms that randomly sprout off other rooms.
I need to create an algorithm that can read in a tile, and figure out how that tile connects with other tiles. I have decided on some restrictions:
- Tiles will require at least one "door" that will be an opening for other tiles.
- Tiles that fit together will share at least one door of the same width.
- A tile that can fit will be rotated and moved until the matching doors align.
- How do I determine the width of a door?
- How do I know if a tile section is unreachable?
- If placing between two tiles, do all doors have to match?
Introduction
I created this blog to detail my code-based creations. Too often, the people in this industry get caught up in the details. "My code is faster than your code!" they'll say, or, "How can you not know how to code a Bucket Sort?" Does an empty string equivalent to a null string? Who cares?
We need to get beyond the point of debating who's right and who's wrong, and remember that coding is an art form. Sure, there are "faster" ways to code things, but in the end there is no "right" or "wrong." Slower code is easier to understand and conceptualize just as faster code is faster. Inline if statements? Who gives a damn.
The stereotypical software engineer is antisocial. He is territorial, judgmental, and does not know how to communicate. He does not see out of his own box. I hope we can move forward and get past this stereotype. We are artists. We create something from nothing. Lets debate our creations with gusto on the ideas behind it, not the brushwork.
We need to get beyond the point of debating who's right and who's wrong, and remember that coding is an art form. Sure, there are "faster" ways to code things, but in the end there is no "right" or "wrong." Slower code is easier to understand and conceptualize just as faster code is faster. Inline if statements? Who gives a damn.
The stereotypical software engineer is antisocial. He is territorial, judgmental, and does not know how to communicate. He does not see out of his own box. I hope we can move forward and get past this stereotype. We are artists. We create something from nothing. Lets debate our creations with gusto on the ideas behind it, not the brushwork.
Subscribe to:
Posts (Atom)