I have been doing some prototype work and learning techniques. Enough to make me take a step back and think about my project goals and design.

I mentioned in a previous "hello" message that I wanted to make a game that took inspiration from an old board game, but is not a clone. However, I'd like to keep a number of similar features. The original game board will be shown in an image at the end. That shows a multiplayer game, but I plan single player and NPCs only. I calculate that it had somewhere around 1400 hexes. I plan to use square tiles, so maybe 40x35 or so.

With a game like this, you want to be able to see all of the board at times for strategic reasons, but focus in on part for tactical purposes. I'm thinking about best practices for a situation where the playing surface is much larger than the viewport, and you need to move around it quickly and not with following a player character or the like.

My initial thought was a scrolling tilemap, and I have been doing some work on that. But I wonder if that's the best approach. Has anyone done or seen an implementation of a "click and drag" for moving a map around, like with online maps and such? With a scroll-wheel zoom as needed.

Other suggestions and possible directions would be welcome. I am an experienced software engineer, and long-time game player, but still in the learning stages with Godot. So don't fear overwhelming me.

duane

Could you expand on that a bit? I'm not exactly following you.

PS: Thanks for the assistance previously on the scrolling stuff.

    Default_User

    Draw the entire board in miniature in one corner (possibly with transparency when the mouse isn't over it), with a shaded region to indicate what's currently displayed on the rest of the screen. Then click on any part of the mini-map to move the main screen to that region, or click-drag to scroll to that region.

    For bonus points, you could use exaggerated animations in the mini-map to show what's happening on the rest of the board. For example, a large image of a ship appears over the mini-map, then shrinks down to the correct scale at the right place to show a new ship has appeared somewhere other than the current view of the main screen.

    I know I've seen that done before, but I can't remember where.

    Oh. A gap in my initial explanation was that rather than a fixed map as in the original, there would be a dynamic one created at initialization. This would allow for variation in play each game.

    Sometimes, when in the early exploratory phases of a software project and you get in the weeds a bit, a good strategy can be to scrap it and start again with a simpler version. I did that.

    I made a new project, that just had a Node2D, with a TileMap and a Camera2D. One interesting thing was that the TileMap had its location with the upper left corner at the center of the screen. See Image 1 below. I hadn't seen that before, but I probably didn't do something correctly, The TileMap was unaltered other than a script attached, and the tiles created programmatically. For now, I corrected it by calculating an offset and setting the TileMap's position so that its center was at the screen center. Feel free to let me know if there's a better method.

    To the Camera2D script, I added the C# version of some camera zoom code I found in my research. Zoom worked perfectly, no moving around as before, it stayed centered. See Image 2.

    Finally, I had some other code for dragging with the mouse. I reproduced that in the TileMap script. It sort of worked. The map would follow the mouse but it lagged badly, so you'd have to drag it several times. Trying to multiple the speed made everything jerky. So I scrapped that and just thought about what I wanted,

    I wanted the TileMap to go where the mouse is. So I changed the code to shift the TileMap position to the current mouse position. That wasn't quite it, because again the TileMap position was its upper corner. So again I had to calculate an offset based on the position of the initial mouse "grab" on the TileMap, and add that offset to the current mouse position. That worked nearly perfectly. Further zoom in or out would stay centered where it was. See Image 3.



    Here is the C# code for the Camera Zoom and Mouse Drag referenced above, in case it's of interest or use to anyone.

    using Godot;
    using System;
    
    public class Tiles : TileMap
    {
       // Declare member variables here. 
       private Vector2 currentMouseCoords;
       private bool dragState = false;
       private Vector2 offset;
    
       // Called when the node enters the scene tree for the first time.
       public override void _Ready()
       {
          this.currentMouseCoords = this.GetGlobalMousePosition();
          const int CELL_SIZE = 128;
    
          Vector2 size = new Vector2(CELL_SIZE, CELL_SIZE);
          this.CellSize = size;
          Texture tex = (Texture)GD.Load("res://Ground2.jpg");
    
          for (int y = 0; y < 10; ++y)
          {
             for (int x = 0; x < 20; ++x)
             {
                int id = this.TileSet.GetLastUnusedTileId();
                this.TileSet.CreateTile(id);
                string name = "Tile" + id.ToString();
                this.TileSet.TileSetName(id, name);
                this.TileSet.TileSetTexture(id, tex);
                this.SetCell(x, y, id);
             }
          }
    
          size = this.GetUsedRect().Size;
          size *= (float)-0.5 * CELL_SIZE;
          this.Position = size;
       }
    
       // Called every frame. 'delta' is the elapsed time since the previous frame.
       public override void _Process(float delta)
       {
          if (this.dragState)
          {
             this.Position = this.GetGlobalMousePosition() + this.offset;
          }
       }
       public override void _Input(InputEvent @event)
       {
          if (@event is InputEventMouseButton eventMouse)
          {
             if (eventMouse.ButtonIndex == (int)Godot.ButtonList.Right)
             {
                if (!this.dragState)
                {
                   this.dragState = true;
                   this.offset = this.Position - this.GetGlobalMousePosition();
                }
                else
                {
                   this.dragState = false;
                }
             }
          }
       }
    }
    using Godot;
    using System;
    
    public class Camera2DControl : Camera2D
    {
       // Declare member variables here.
       private Vector2 zoomMin;
       private Vector2 zoomMax;
       private Vector2 zoomSpeed;
       private Vector2 desZoom;
       private float lerpSpeed;
    
       // Called when the node enters the scene tree for the first time.
       public override void _Ready()
        {
          this.zoomMin = new Vector2((float).1, (float).1);
          this.zoomMax = new Vector2((float)5.0, (float)5.0);
          this.zoomSpeed = new Vector2((float).2, (float).2);
          this.desZoom = this.Zoom;
          this.lerpSpeed = (float)0.2;
       }
    
       public override void _Process(float delta)
       {
          Vector2 update;
          update.x = Mathf.Lerp(this.Zoom.x, desZoom.x, lerpSpeed);
          update.y = Mathf.Lerp(this.Zoom.y, desZoom.y, lerpSpeed);
          //Console.WriteLine(update);
          this.Zoom = update;
       }
       private void changeDesZoom(int dir)
       {
          desZoom.x = Mathf.Clamp(desZoom.x + (zoomSpeed.x * dir), zoomMin.x, zoomMax.x);
          desZoom.y = Mathf.Clamp(desZoom.y + (zoomSpeed.y * dir), zoomMin.y, zoomMax.y);
       }
       public override void _Input(InputEvent @event)
       {
          if (@event is InputEventMouseButton eventMouse)
          {
             if (eventMouse.ButtonIndex == (int)Godot.ButtonList.WheelDown)
             {
                changeDesZoom(-1);
             }
             else if (eventMouse.ButtonIndex == (int)Godot.ButtonList.WheelUp)
             {
                changeDesZoom(1);
             }
          }
       }
    }