Tutorial How to make a map game

Discussion in 'Resources' started by mcdorli, Nov 16, 2015.

?

Was this helpful/interesting

  1. Yes

    100.0%
  2. No

    0 vote(s)
    0.0%
Thread Status:
Not open for further replies.
  1. Offline

    mcdorli

    In this tutorial series, I want to show you, how to use the Bukkit API, and some basic java stuff, to create games with the map items. This tutorial doesn't involve using any librarys, and the code should be compatible with java 1.7 and up.

    Disclaimer: You need to know immediate java, I'm not going to explain anything about basic java stuff, like how to render to a buffered image.

    Part 1 (open)
    I'm not going to use the map canvas' setPixel() method, basically, because every color is deprecated, and it is easier to render to a buffered image and then render that to the map itself.

    So, first you need a basic setup with the Bukkit API. Gte the mapView using the Bukkit#getMap(int id).

    Code:
    @Override
    public void onEnable() {
         
        MapView map = Bukkit.getMap((short) 0);
        //this method is deprecated, don't forget to put a deprecation warning before the method
         
        if (map == null) {   //Null check
            getLogger().info("[MapGame] Failed to get map");
            return;  //You could throw here an exception, but I'm not going to care about that much
        }
         
    }
    
    You need to remove all the mapRenderers from the mapView, then add your custom one. MapRenderers do all the rendering to the map, the render method gets called every tick, so you need to be very careful about the rendering time.

    Code:
    for (MapRenderer r : map.getRenderers()) {
        map.removeRenderer(r);
    }
         
    map.addRenderer(new Renderer()); //Renderer needs to extend the MapRenderer class
    
    The Renderer class:

    Code:
    public class Renderer extends MapRenderer {
    
        @Override
        public void render(MapView view, MapCanvas canvas, Player player) {
             //Here comes the rendering code
        }
    
    }
    
    
    As I said above, we're going to use a bufferedImage, to render things at. This is mainly because it is more flexible. It allows scaling images, it is faster, etc. So, we create one in the MapRenderer class, and get the Graphics out of it.

    Code:
    BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB); //The width and height MUST be 128, because that is the size of the maps.
    Graphics g = image.getGraphics();
    
    //Note: if you put these in the render method, then you will create a new bufferedImage object every tick,
    //wich is acceptable, but definitly not good in terms of memory management, place them on the class-level
    
    After we have done all off this, we just need to render the image to the canvas, for testing purposes, I will draw a red rectangle in the bufferedImage.

    Code:
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, 128, 128);   //This works the same, as drawing on a JPanel
    g.setColor(Color.RED);
    g.fillRect(10, 10, 108, 108); //This should leave us 10 pixes margin every side.
         
    canvas.drawImage(0, 0, image);
    
    To get the actual map, do /give <name> 358, this will give you the corresponding map.

    Note: If you never created a map on the server before, then this should work by right clicking with a normal empty map

    As seen on this image:

    [​IMG]

    ...everything works as intended. No console errors.

    This was the last part we used the Bukkit API in the rendering stuff. The next part is about animation.


    Part 2 (open)

    So, in the last part, we created the "display". In this tutorial, we're going to use it to animate something.

    For this, you need to add something called "delta time" to the rendering process. This will measure the amount of time taken to render things out. This technique is often used in games, to avoid the slowing down of the game with low fps. We need it here, because rendering to maps can be very hard for the CPU, and we want to move things equally. One example for this not being used is the flappy bird. My phone was capped to 30 frames every second, so it was actually slower for me than on other phones. It was a lot easier to manage.

    First, you need to define two variables, both are long, one is for storing the current time, the other one is for the last time we rendered something. (I deleted all the rendering stuff).

    Code:
    long currentFrame = 1;
    long lastFrame = System.currentTimeMillis();  //<-this comes at the class-level
    
    //....
    
    //This comes in the render method
    
    currentFrame = System.currentTimeMillis();  //We get the current time
        
    long deltaTime = currentFrame - lastFrame;  //Then we substract the last time from it, so we get the time
    //difference between two calls
    float speedModifier = (float) deltaTime / 1000; //We will multiply every movement with the speedModifier
        
    lastFrame = currentFrame; //Last we set the last Frame to the current frame
    
    Now that this is done, we get into the real animation. We are going to test this system out with a bouncing ball. We need to declare the position and the speed of the ball, then every frame, we're going to add the speed to the balls position, and if the ball hits the edges, then it going to bounce off. This is really close to the PONG, literally, 90% of a Pong programs code is the ball itself.

    Variable declarations (class-level):

    Code:
    int side = 5; //The size of the ball (It's going to be a square, because it's minecraft)
    
    Random rand = new Random();
    
    int x = rand.nextInt(128 - side * 2) + side; //The position of the ball, I give it a random position, so it is a bit
    int y = rand.nextInt(128 - side * 2) + side; //more awesome
    
    int vy = 100; //The max speed it is going by. This is what it should go in 1 second actually.
    int vx = 100;
    
    The math and rendering (inside the render method):

    Code:
    //setting the new position
    x += vx * speedModifier;
    y += vy * speedModifier;
    
    
    //Basic collision detection with the map edges
    if (x - side < 0) {
        x = side;
        vx *= -1;
    }
    if (x + side > 128) {
        x = 128 - side;
        vx *= -1;
    }
    
    if (y - side < 0) {
        y = side;
        vy *= -1;
    }
    if (y + side > 128) {
        y = 128 - side;
        vy *= -1;
    }
    // Filling the background with white
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, 128, 128);
    //Drawing the ball
    g.setColor(Color.RED);
    g.fillRect(x - side, y - side, 2 * side, 2 * side);
    
    If you run this, it should display a rectangle bouncing around in the map.

    In the next part, I will teach you to handle inputs.


    (I appreciate any language corrections too)

    EDIT by Timtower: merged threads.
     
    Last edited: Nov 16, 2015
    DoggyCode™ and teej107 like this.
  2. Offline

    teej107

    I haven't played with MC Maps but this tutorial got me interested. Can't they only display like 256 colors though?
     
  3. Offline

    mcdorli

    I'm not sure, I think yeah. But they are still fun to mess around with, and they actually manages colors for you, you don't need to use specific colors.

    With this setup, you can create anything, that is possible with java. Before this tutorial I made a Pong,
    The biggest downside is that they really suck the power out of the CPU.
     
    Last edited: Nov 16, 2015
  4. Offline

    teej107

    I'm wondering how well it could handle if I port over one of my Java games. It wouldn't be that hard to do it.
     
  5. Offline

    mcdorli

    If it is a java2d game, then sinply instead of rendering to a canvas or jpanel, render to a the bufferedimage mentioned above. The input is going to be very awkward, and only the wasd and the space key is going to be usable (and maybe the arrows)
    Even doom like games are possible
     
  6. Offline

    teej107

    Yeah it is a 2D game. Everything I render is using JPanel's paintComponent method and I use the Graphics object passed to it. I'm not too worried about the rendering. It's just making the game timer to work with Bukkit that I worry about:p
     
  7. Offline

    mcdorli

    It is capped at 20 fps, wich can be annoying.
     
  8. Offline

    teej107

    Yeah that's my worry :p
     
  9. Offline

    FabeGabeMC

    @mcdorli This seems interesting... gonna play around with these sometime. ( ͡° ͜ʖ ͡°)
     
Thread Status:
Not open for further replies.

Share This Page