Gorbles

[DevLog] Honeycomb

Recommended Posts

promo2.png

 
This started off a bit of a silly project. There wasn't really much "game" to this, it originally started as experimenting with Graphics2D in Java.

http://gorbles.itch.io/honeycomb-beta

That's the new beta release, with all the updates listed below and on that page.


Milestones
 

ALPHA (DONE) ::

  • The initial goal was something simple, get a renderer on its own thread running the game graphics.
  • Add a game state for each render group; pre-game, ingame, end-game, etc.
  • Mess about with KeyListeners and the like, develop a system to delegate them down to the data model.
  • At this point I was just testing it by adding fun spinning 2D shapes that spin.
  • Let's make it so you can click on them too!
  • As the game idea formed in my head, I added a variable lifetime to each shape so they "die" eventually.

BETA (IN-PROGRESS) ::

  • Make the UI not terrible. (DONE)
  • Make the ingame art not terrible. (DONE?)
  • Build a graphical font system! (DONE)
  • Add a tiny amount of balance to the gameplay. Clicks give more points than dragging. (DONE)
  • Add bigger shapes that have unique effects (for <good marketing> and <player retention> :P).
  • Add a countdown to game start.
  • Add more art, a friend suggested bees. OH GOD THE BEES.

RELEASE ::

  • Polish?
  • Performance pass (game is quite CPU-bound, fixed step renderer struggles on low-clocked CPUs)


Java Stuff
 

I'm not here to blow any minds, really. My next developer (b)log will probably be more around the art side of things, building interfaces, practising complicated pixel art scenes, and so on. I've got a lot of that I need to do to build a working game with some of the tech I have.


The developer intent behind this was to explore the Graphics2D API, and to push my knowledge with that forwards. I'm pretty adept in building data-driven systems (my day job is web services over Tomcat, etc, and many other bits and pieces) and I've done enough work with the javax.Swing library to never make me want to do any more work in Swing (you only need a few hours in this to put you off for life, but I've managed a few years). Will probably add more posts to this with the details of some of my nicer (?!) classes.


Art Stuff
 

I mainly do pixel graphics; I don't have a tablet nor do I have the time to justify that kinda purchase. Pixel art I can do on my phone (found a great app for it) and I can practise it well enough when bored at my home PC. I don't always expect it to be great, but it matches up well with simple geometry, so why the hell not? :)

Edited by Gorb

Share this post


Link to post
Share on other sites

Updated with new logo, beta release and improved notes / progress.

 

Going to spend a bit of time cleaning up the code; this project doesn't really need it but the tech I'm building will serve as a base in future. Going to aggregate some of the font rendering code and shove it into a factory that returns nice single BufferedImages (removes the need for a lot of tedious looping) and maybe look at the render loop to see if I can do some frame-predictive stuff.

 

v7_2.png

v7_3.png

Share this post


Link to post
Share on other sites

Haven't done a huge amount this weekend, was too busy with Life™ and such. Spent a lot of the last week doing internal refactoring and making a lot of the UI generation less horrific.

 

LayeredImageFactory now handles a lot of the font / word generation, short term bug is that I broke the default colour for the words on my UI, but to compensate I've got keyword highlighting in there and intelligent cropping of whole words inside paintable areas (it'll start new lines, etc):

 

example3.png

 

Next up is improving the main ingame painting loop as well as caching a lot of the generated BufferedImages. It's a bit expensive to be redrawing everything (including new object instantiation) every frame so I'm going to implement a few things to handle that. All of this is kinda a distraction from having a serious look at the renderer as I'm seriously not looking forward to even the most basic of physics in that regard. Thread.sleep(1000 / 60) is my preferred limit, haha :D

Share this post


Link to post
Share on other sites

A short post here, but I put most of what I wanted to say in a blog post - https://gorbles.blogspot.co.uk/2015/11/honeycomb.html.

 

Next on the list is improving the render cycle as well as implementing a global messaging system that'll allow me to delegate game events to the relevant subsystems in a much nicer way. Trying to minimise dependence on listeners as they can pile up quite quickly, and quite messily.

Share this post


Link to post
Share on other sites

The hex maths was actually the easiest (bit of a copy-and-paste job), but I took the time to um, re-acquaint myself with the horror that is basic maths to figure out what was going on. I added some comments to remind myself:

// x == radius * cosine of angle * iteration in radians
// y == radius * sine of angle * iteration in radians
int x = (int) (size * Math.cos(Math.toRadians(360 / n * i)));
int y = (int) (size * Math.sin(Math.toRadians(360 / n * i)));

Where n is the number of sides. This works for any equilateral polygon, you just feed the x and y into a Point of your language's choosing (and then into a Polygon / Shape). The radius of any equilateral hexagon is the same as the length of an edge.

 

Each Hexagon object contains three Polygons (I actually generated three during each iteration of the above loop), one for the base Hexagon and one for each transform I applied. I have a (continual) rotation transform and a (one-off) scalar (for the enlarge-on-mouseover you can see in the screenshots). That way I control the shape and size of the polygon from the quoted code there (across all transforms) and all I need to do is modify the transforms themselves once (as that code is defined elsewhere).

 

The nicest part. The hard work came after, haha.


(unless you're referring to the spawn logic which is moderately horrific but I'm quite proud of it)

Share this post


Link to post
Share on other sites

The grid is a bit of a mess. I actually messed around with getting a random edge of a hexagon and spawning a new hexagon against that edge, but something to do with how Java handles rendering borders over a thickness of 1 messed up my maths a bit. I got it working, but it wasn't pixel-perfect at all.

 

So I went the hacky route.

 

An even / odd boolean to control the row I was on. If it was even, the offset was 0, and if it was odd, the offset was relative to the width of the hexagon. This allowed me to layer subsequent rows against each other with no overlap.

 

I then check the length of the row against the maximum panel width dividing against the width of the hexagons I'd added so far. Row end reached? Flip the even / odd boolean, and start again. Keep going until there was no y-axis space to expand into. Pretty sure I hardcoded the height in an ugly manner. One mo, will look up the code.

 

EDIT:

 

Okay, gosh, it was even hackier than that. Lots of boolean checks, haha. Some pixel-fudging of spawn positions, too. Didn't work nicely if I messed with the size of the hexagons too much (though again that was mainly due to border-thickness interaction).

int offsetX = 0;
int offsetY = 0;
int count = 0;
int line = 0;
boolean even = true;
boolean finished = false;
Hexagon last = null;
while(!finished) {
    if(even) {
        BasicHexagon h = new BasicHexagon();
        int life = seed.nextInt(h.getLifeSeconds() + 1);
        h.setLifeSeconds(life);
        h.setLifeMilliSeconds(life * 1000);
        if(h.getLifeSeconds() == 0) {
            h.setLifeSeconds(1);
        }
        
        maxScore = maxScore + h.getLifeSeconds() * CLICK_MULTIPLIER;
        int boundsWidth = (int) Math.ceil(h.getBase().getBounds2D().getWidth());
        int boundsHeight = (int) Math.ceil(h.getBase().getBounds2D().getHeight());
        // set the initial offset for the top-left hexagon
        if(count == 0) {
            offsetX = (boundsWidth / 2) + 2;
            offsetY = (boundsHeight / 2) + 2;
        }

        h.getBase().translate(offsetX, offsetY);
        tiles.add(h);
        count = count + 1;
        even = false;
        last = h;
    } else {
        // up the offset on the X-axis for next hexagon spawn
        offsetX = offsetX + ((int) last.getBase().getBounds2D().getWidth() + last.getHexEdgeWidth()
                + (last.getHexBorderWidth() * 2));
        if(offsetX > gameWidth - (last.getHexEdgeWidth()
                + last.getHexBorderWidth())) {
            if(offsetY > gameHeight - ((int) last.getBase().getBounds2D().getHeight()
                    + last.getHexBorderWidth())) {
                finished = true;
                break;
            }

            line = line + 1;
            offsetY = offsetY + (int) Math.ceil(last.getBase().getBounds2D().getHeight() / 1.5) -
                    (last.getHexBorderWidth() * 3 / 2);
            int boundsWidth = (int) Math.ceil(last.getBase().getBounds2D().getWidth());
            if(line % 2 == 0) {
                offsetX = (boundsWidth / 2) + 2;
            } else {
                offsetX = (boundsWidth / 2) + 2 + ((int) Math.ceil(last.getHexEdgeWidth() * 1.5) +
                        (last.getHexBorderWidth()));    
            }                    
        }

        even = true;
    }
}

Share this post


Link to post
Share on other sites

Haha, awesome. I love that.

 

The reason I asked (sorry, should have started with this) is that I'm messing with a hex-based game myself but ended up starting with the three-axis version from here:

http://www.redblobgames.com/grids/hexagons/

 

and it was blowing my mind / ended up buggy so I was wondering what you ended up going with.

 

I should get up the courage to just do stuff like you did instead of trying to over-engineer things. :tup:

Share this post


Link to post
Share on other sites

Hahaha, I have that site bookmarked (not that specific page) and I didn't think to check it out beforehand. Then again, as I said, a lot of this came together pretty much overnight. Over-engineering is a problem area I think all programmers go through - I've been guilty of it plenty of times in the past. In fact, I actually started working on these smaller, rapid projects as a way to stop getting bogged-down in overengineering things :)

 

My problem is also my strength: I work at something til it works. I've done quite a lot of "re-inventing stuff people have already made", but it helps me get a better understanding of both Java and how to use it effectively.

 

Nothing like graphics programming to make it obvious when something is badly-coded!

Share this post


Link to post
Share on other sites

Been a bit slow on this, RL consequences hit with a vengeance sometimes. Haven't had much headspace for personal projects. Done some bits here and there, but need to fold all my experimental code back into the Honeycomb! project.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now