📜 ⬆️ ⬇️

Participation in the competition LudumDare # 33

Despite the fact that for all the two days of the competition, the fantastic idea of ​​the game never came to me, I want to share some insights that were made in a couple of days. Perhaps the article will find its reader, and someone still likes pure-java instead of fancy engines.

This is my fifth part, and every single time I write every little letter from scratch. It so happened that while I am writing the basic methods for working with graphics (there are not so many of them as it seems), I’ll invent what to write. Sometimes even something comes out.

image
')
If you do not go into details, the game is quite primitive. As the day progresses, you control a slow monster that is attacked from all sides by knights. But as soon as night comes, the situation changes dramatically, now you are a very smart one-eyed monster who is able to exterminate knights. But everything is not so rosy, since I introduced autocontrol into aggressive behavior (at night), which did almost everything for you: and killed, and looked for new victims-knights.

Interesting moments (for developers) just a couple, this is probably the generation of tiles and night vision, which greatly distorts the appearance, something similar to the fish eye effect. The effect, by the way, turned out by itself, from boredom. The fact is that I tormented myself with the thought that I never came up with a suitable idea for the game, but it was too late to stop, and I did, did, did.

Let's look at these moments in more detail. For starters, night vision:

image

As usual, the rendering first occurs in the offscreen buffer, and as soon as all the game elements are drawn, post-processing such as noise, distortion, etc. should be done.

Post render
public void postRender(Bitmap screenBitmap, Graphics2D g2d) { //     int[] pixels = screenBitmap.pixels; //   for (int i = 0; i < pixels.length; i++) { //     int x = i % screenBitmap.w - GameComponent.WIDTH / 2; int y = i / screenBitmap.w - GameComponent.HEIGHT / 2; //            //    x    ,      //       ,     0..255 double angle = (x / (double) screenBitmap.h * y / (double) screenBitmap.h) * Math.PI * 2.0 * (255 - dayFactor) / 255.0; //      int xx = (int) (x * Math.cos(angle) - y * Math.sin(angle)) + GameComponent.WIDTH / 2; int yy = (int) (y * Math.cos(angle) + x * Math.sin(angle)) + GameComponent.HEIGHT / 2; //      ,    if (xx >= 0 && yy >= 0 && xx < screenBitmap.w && yy < screenBitmap.h) { int c = pixels[xx + yy * screenBitmap.w]; int r = (c >> 16) & 0xff; int g = (c >> 8) & 0xff; int b = (c >> 0) & 0xff; //   gray scale,    //https://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%82%D0%B5%D0%BD%D0%BA%D0%B8_%D1%81%D0%B5%D1%80%D0%BE%D0%B3%D0%BE int m = (r * 30 + g * 59 + b * 11) / 100; //          r = (r + m) / 2 * dayFactor / 255; g = (g + m) / 2 * dayFactor / 255; b = (b + m) / 2 * dayFactor / 255; //     ,         postData[i] = 0xff << 24 | r << 16 | g << 8 | b; } else { //    ,  " "       int rnd = (int) (random.nextDouble() * 16); postData[i] = 0xff << 24 | (0x4C + rnd) << 16 | rnd << 8 | rnd; } } //         ,     . for (int i = 0; i < postData.length; i++) { pixels[i] = postData[i]; } g2d.setColor(Color.WHITE); g2d.drawString("score: " + score, 10, GameComponent.HEIGHT - 10); } 


I think there should be no questions. Of course, you can play with some values ​​and achieve a more pleasant effect.

33 MB big gif
image

Now let's about tile generation. In principle, there are no secrets here, if you have ever been engaged in the creation of 2D games, then you know that to transfer one texture to another you need 16 tiles. Our task is to generate these tiles. So let's think, each tile has 4 corners, it is necessary to make 16 types of textures, taking into account transitions from one tile to another. Here is an example of the generated textures for this method, except with the conversion to isometry.

image

But we will now consider a simple example for a game without isometry.

Texture generator
  //       1616,   16       public static BufferedImage[] generate(int[] t0, int[] t1, int sz) { double iSz = 1.0 / sz; BufferedImage[] result = new BufferedImage[16]; for (int i = 0; i < 16; i++) { //        result[i] = new BufferedImage(sz, sz, BufferedImage.TYPE_INT_RGB); int[] data = new int[sz * sz]; //  4     //  ,        i   4  int a = (i >> 0) & 1; int b = (i >> 1) & 1; int c = (i >> 2) & 1; int d = (i >> 3) & 1; //   for (int y = 0; y < sz; y++) { double yp = y * iSz; for (int x = 0; x < sz; x++) { double xp = x * iSz; //   ab      t = xp double ab = a + (b - a) * xp; //  cd    t = xp double cd = c + (d - c) * xp; //   ab  cd     t = yp double val = ab + (cd - ab) * yp; //...  -    val //       ,        val if (val < 0) val = 0; if (val > 1.0) val = 1.0; //    1  2      t = val int c0 = t0[x + y * sz]; int c1 = t1[x + y * sz]; int col = Mth.lerpRGB(c0, c1, val); //     data[x + y * sz] = col; } } result[i].setRGB(0, 0, sz, sz, data, 0, sz); } return result; } 


And now the most tasty: how to display the resulting tiles based on the generated map? The answer is very, very simple:

Water tile render method
  public void render(Graphics2D g2d, Level level, int x, int y, int xOffs, int yOffs) { int xx = x >> 4; int yy = y >> 4; int t = 0; //           if (level.getTile(xx, yy) != Tile.water) t += 1; //     if (level.getTile(xx + 1, yy) != Tile.water) t += 2; //     if (level.getTile(xx, yy + 1) != Tile.water) t += 4; //      if (level.getTile(xx + 1, yy + 1) != Tile.water) t += 8; g2d.drawImage(Art.waterToGrassTiles[t], x - xOffs, y - yOffs, null); } 


That's all. Nothing fantastic, but I get a lot of pleasure from such trifles, which I advise you.

Source: https://habr.com/ru/post/269527/


All Articles