Bladeren bron

add `roguelike` example :eyes:

Ethosa 4 jaren geleden
bovenliggende
commit
c1696f9acc

+ 1 - 1
.github/workflows/test.yml

@@ -57,7 +57,7 @@ jobs:
       - name: Build examples
         run: |
           cd examples
-          for dir in hello_world calculator novel snake screensaver; do
+          for dir in hello_world calculator novel snake screensaver roguelike; do
             (
               cd "$dir"
               nim c main.nim

+ 1 - 1
README.md

@@ -9,7 +9,7 @@ The Nim GUI/2D framework based on OpenGL and SDL2.
 [![time tracker](https://wakatime.com/badge/github/Ethosa/nodesnim.svg)](https://wakatime.com/badge/github/Ethosa/nodesnim)
 [![test](https://github.com/Ethosa/nodesnim/workflows/test/badge.svg)](https://github.com/Ethosa/nodesnim/actions)
 
-<h4>Stable version - 0.3.1</h4>
+<h4>Stable version - 0.3.2</h4>
 </div>
 
 ## Install

BIN
examples/roguelike/assets/colored_tilemap.png


BIN
examples/roguelike/assets/player.png


+ 90 - 0
examples/roguelike/main.nim

@@ -0,0 +1,90 @@
+import
+  nodesnim,
+  random
+
+Window("Roguelike", 480, 240)
+env.setBackgroundColor(Color("#e0f8cf"))
+
+var
+  tileset = TileSet("assets/colored_tilemap.png", Vector2(16, 16), GL_RGBA)
+  charapter = load("assets/player.png", GL_RGBA)
+
+const
+  PLAYER_SPEED: float = 4
+  LEVEL_WIDTH: float = 100
+  PLAYER_SIZE: float = 16
+
+build:
+  - Scene main:
+    - TileMap map:
+      call setTileSet(tileset)
+      call resizeMap(newVector2(LEVEL_WIDTH, 15), 2)
+
+    # Player
+    - KinematicBody2D player:
+      call move(300, 120)
+      - Sprite player_sprite:
+        call setTexture(charapter)
+      - CollisionShape2D player_collision:
+        call resize(PLAYER_SIZE, PLAYER_SIZE)
+        call move(-8, -8)
+
+    # Collision
+    - CollisionShape2D top:
+      call resize(LEVEL_WIDTH*16, 1)
+      call move(0, -1)
+    - CollisionShape2D bottom:
+      call resize(LEVEL_WIDTH*16, 1)
+      call move(0, 240)
+    - CollisionShape2D left:
+      call resize(1, 240)
+      call move(PLAYER_SIZE, 0)
+    - CollisionShape2D right:
+      call resize(1, 240)
+      call move(LEVEL_WIDTH*16, 0)
+
+    - Camera2D camera:
+      call setLimit(0, 0, LEVEL_WIDTH*16, 240)
+      call setCurrent()
+      call setTarget(player)
+      call enableSmooth()
+
+# Draw grass
+for i in 0..512:
+  map.drawTile(rand(99), rand(14), newVector2(0, 1), 1)
+
+# Draw trees
+for i in 0..128:
+    map.drawTile(rand(99), rand(14), newVector2(rand(13..16).float, 0), 1)
+
+# Draw houses
+for i in 0..32:
+    var
+      pos = Vector2(rand(99).float, rand(14).float)
+      collider = CollisionShape2D()
+    map.drawTile(pos.x.int, pos.y.int, newVector2(rand(13..16).float, 2), 1)
+    collider.resize(PLAYER_SIZE, PLAYER_SIZE)
+    collider.move(pos.x*PLAYER_SIZE, pos.y*PLAYER_SIZE)
+    main.addChild(collider)
+
+
+# Movement
+Input.addKeyAction("forward", "w")
+Input.addKeyAction("backward", "s")
+Input.addKeyAction("right", "d")
+Input.addKeyAction("left", "a")
+
+player@onInput(self, event):
+  if Input.isActionPressed("right"):
+    player.moveAndCollide(Vector2(PLAYER_SPEED, 0))
+  elif Input.isActionPressed("left"):
+    player.moveAndCollide(Vector2(-PLAYER_SPEED, 0))
+
+  if Input.isActionPressed("forward"):
+    player.moveAndCollide(Vector2(0, -PLAYER_SPEED))
+  elif Input.isActionPressed("backward"):
+    player.moveAndCollide(Vector2(0, PLAYER_SPEED))
+
+
+addMainScene(main)
+windowLaunch()

+ 1 - 0
examples/roguelike/nim.cfg

@@ -0,0 +1 @@
+--path:"../../src"

+ 1 - 1
nodesnim.nimble

@@ -1,7 +1,7 @@
 [Package]
 name = "nodesnim"
 author = "Ethosa"
-version = "0.3.1"
+version = "0.3.2"
 description = "The Nim GUI/2D framework based on OpenGL and SDL2."
 license = "MIT"
 srcDir = "src"

+ 1 - 0
src/nodesnim/core/image.nim

@@ -4,6 +4,7 @@ import
   ../thirdparty/sdl2,
   ../thirdparty/sdl2/image,
 
+  tileset,
   vector2
 
 when defined(debug):

+ 1 - 2
src/nodesnim/core/tileset.nim

@@ -21,7 +21,7 @@ proc TileSet*(img: string, tile_size: Vector2Obj, mode: Glenum = GL_RGB): TileSe
     surface = image.load(img)  # load image from file
     textureid: Gluint = 0
   when defined(debug):
-    if surface == nil:
+    if surface.isNil():
       error("image \"", img, "\" not loaded!")
       return
 
@@ -34,7 +34,6 @@ proc TileSet*(img: string, tile_size: Vector2Obj, mode: Glenum = GL_RGB): TileSe
 
   glTexImage2D(GL_TEXTURE_2D, 0, mode.GLint, surface.w,  surface.h, 0, mode, GL_UNSIGNED_BYTE, surface.pixels)
   glBindTexture(GL_TEXTURE_2D, 0)
-
   result = TileSetObj(
     grid: tile_size,
     size: Vector2(surface.w.float, surface.h.float),

+ 3 - 1
src/nodesnim/nodes2d/collision_shape2d.nim

@@ -115,6 +115,7 @@ when defined(debug):
       for vec2 in self.polygon:
         glVertex3f(x + vec2.x, y - vec2.y, self.z_index_global)
       glEnd()
+    glColor4f(1, 1, 1, 1)
 
 
 method duplicate*(self: CollisionShape2DRef): CollisionShape2DRef {.base.} =
@@ -201,7 +202,8 @@ method isCollide*(self, other: CollisionShape2DRef): bool {.base.} =
   of COLLISION_SHAPE_2D_RECTANGLE:
     case other.shape_type:
       of COLLISION_SHAPE_2D_RECTANGLE:
-        return Rect2(other.global_position, other.rect_size).intersects(Rect2(self.global_position, self.rect_size))
+        return Rect2(other.global_position, other.rect_size).intersects(Rect2(self.global_position, self.rect_size)) or
+               Rect2(self.global_position, self.rect_size).intersects(Rect2(other.global_position, other.rect_size))
       of COLLISION_SHAPE_2D_CIRCLE:
         return Rect2(self.global_position, self.rect_size).isCollideWithCircle(
           other.global_position.x + other.x1,

+ 7 - 10
src/nodesnim/nodes2d/kinematic_body2d.nim

@@ -95,26 +95,23 @@ method moveAndCollide*(self: KinematicBody2DRef, vel: Vector2Obj) {.base.} =
   ## Arguments:
   ## - `vel` is a velocity vector.
   self.move(vel)
-  self.calcGlobalPosition()
   if self.has_collision:
-    var scene = self.getRootNode()
-    self.collision_node.calcGlobalPosition()
+    let scene = self.getRootNode()
 
     for node in scene.getChildIter():
       if node.kind == COLLISION_SHAPE_2D_NODE:
         if node == self.collision_node:
           continue
-        if self.collision_node.isCollide(node.CollisionShape2DRef):
+
+        # TODO: normal algorithn
+        let collision_node = node.CollisionShape2DRef
+        if self.collision_node.isCollide(collision_node):
           self.move(-vel.x, 0)
-          self.calcGlobalPosition()
-          self.collision_node.calcGlobalPosition()
 
-          if self.collision_node.isCollide(node.CollisionShape2DRef):
+          if self.collision_node.isCollide(collision_node):
             self.move(vel.x, -vel.y)
-            self.calcGlobalPosition()
             self.collision_node.calcGlobalPosition()
 
-          if self.collision_node.isCollide(node.CollisionShape2DRef):
+          if self.collision_node.isCollide(collision_node):
             self.move(-vel.x, 0)
-            self.calcGlobalPosition()
             self.collision_node.calcGlobalPosition()

+ 5 - 5
src/nodesnim/nodes2d/node2d.nim

@@ -74,15 +74,15 @@ method draw*(self: Node2DRef, w, h: GLfloat) =
 
 method move*(self: Node2DRef, x, y: float) =
   ## Moves Node2D object by `x` and `y`.
-  self.position.x += x
-  self.position.y += y
-  self.timed_position = self.position
+  self.timed_position.x += x
+  self.timed_position.y += y
+  self.position = self.timed_position
 
 
 method move*(self: Node2DRef, vec2: Vector2Obj) =
   ## Moves Node2D object by `vec2`.
-  self.position += vec2
-  self.timed_position = self.position
+  self.timed_position += vec2
+  self.position = self.timed_position
 
 
 method duplicate*(self: Node2DRef): Node2DRef {.base.} =

+ 10 - 5
src/nodesnim/nodes2d/tilemap.nim

@@ -45,22 +45,27 @@ method draw*(self: TileMapRef, w, h: GLfloat) =
   var viewport = @[
       abs(x + w/2).int div self.tileset.grid.x.int, abs(y - h/2).int div self.tileset.grid.y.int,
       abs(x - w/2).int div self.tileset.grid.x.int, abs(y + h/2).int div self.tileset.grid.y.int]
+  if viewport[3] >= self.map_size.y:
+    viewport[3] = self.map_size.y-1
+  if viewport[2] >= self.map_size.x:
+    viewport[2] = self.map_size.x-1
 
   glEnable(GL_TEXTURE_2D)
-  if self.mode == TILEMAP_2D:
+  case self.mode
+  of TILEMAP_2D:
     for z in 0..<self.map_size.z:
-      for x1 in viewport[0]..viewport[2]+1:
-        for y1 in viewport[1]..viewport[3]+1:
+      for x1 in viewport[0]..viewport[2]:
+        for y1 in viewport[1]..viewport[3]:
           let pos = x1+y1*self.map_size.x + self.map_size.x*self.map_size.y*z
           if not self.map[pos].isNil():
             let
               posx = x + self.tileset.grid.x*x1.float
               posy = y - self.tileset.grid.y*y1.float
             self.tileset.draw(self.map[pos].x, self.map[pos].y, posx, posy)
-  else:
+  of TILEMAP_ISOMETRIC:
     for z in 0..<self.map_size.z:
       for y1 in viewport[1]..viewport[3]:
-        for x1 in viewport[1]..viewport[2]:
+        for x1 in viewport[0]..viewport[2]:
           let
             pos = x1+y1*self.map_size.x + self.map_size.x*self.map_size.y*z
           if not self.map[pos].isNil():