SNIPPETS

Engine Initialization

  # mygame.nim

  import
      nimgame2 / [
        nimgame,
        settings,
      ]
      mainscene # your first scene


  game = newGame()
  if game.init(w = 640, h = 480, title = "My Awesome Game"):
      game.scene = newMainScene() # your scene constructor
      game.run()

Scene Initialization

  # mainscene.nim

  import
    nimgame2 / [
      nimgame,
      entity,
      scene,
      settings,
      texturegraphic,
      types
    ]


  type
    MainScene = ref object of Scene
      myGraphic: TextureGraphic
      myEntity: Entity


  proc init*(scene: MainScene) =
    # don't forget the parent initialization!
    Scene(scene).init()

    # create the graphic
    scene.myGraphic = newTextureGraphic()
    # load graphic file
    discard scene.myGraphic.load("data/gfx/my_image.png")

    # create the entity
    scene.myEntity = newEntity()
    # assign the graphic to the entity
    scene.myEntity.graphic = scene.myGraphic

    # add your entity to the scene
    scene.add(scene.myEntity)


  proc free*(scene: MainScene) =
    # free the texture
    scene.myGraphic.free()


  proc newMainScene*(): MainScene =
    # scene constructor
    new result, free
    result.init()


  method event*(scene: MainScene, event: Event) =
    # call it if your entities have their own event handlers
    scene.eventScene(event)
    # if you want to handle some events manually, you do it here
    if event.kind == KeyDown:
      case event.key.keysym.scancode:
      of ScancodeEscape:
        # stop the engine (see nimgame2/settings.nim)
        gameRunning = false
      else: discard


  method render*(scene: MainScene) =
    # don't forget to call the parent render!
    scene.renderScene()
    # if you want to draw something on top of a rendered scene,
    # you do it here


  method update*(scene: MainScene, elapsed: float) =
    # don't forget to call the parent update!
    scene.updateScene(elapsed)
    # scene-level logic goes here

Entity Initialization

  # myentity.nim

  import
    nimgame2 / [
      nimgame,
      entity,
      texturegraphic,
    ]


  type
    MyEntity* = ref object of Entity


  proc init*(entity: MyEntity, graphic: TextureGraphic) =
    # don't forget to call the parent initialization!
    entity.initEntity()
    # assign the graphic
    entity.graphic = graphic
    # set the center point
    entity.centrify()
    # assign the physics only if you need it
    entity.physics = defaultPhysics


  proc newMyEntity*(graphic: TextureGraphic): MyEntity =
    # entity constructor
    new result
    result.init(graphic)


  method update*(entity: MyEntity, elapsed: float) =
    # don't forget to call the parent update!
    entity.updateEntity(elapsed)
    # entity-level logic goes here

Keyboard Input

  # somewhere in the update method

    let move = 100 * elapsed
    if ScancodeLeft.down: scene.player.pos.x -= move
    if ScancodeRight.down: scene.player.pos.x += move
    # the key was pressed
    if ScancodeSpace.pressed: scene.player.startCharging()
    # the key was relased
    if ScancodeSpace.released: scene.player.discharge()

Mouse Input

  # somewhere in the update method

    let mousePos = mouse.abs
    if Button.left.down: scene.player.fireTo(mousePos)
    # Mouse buttons (Button enum): left, middle, right, x1, x2

Joystick Input

  # somewhere in the initialization routine

    for i in 0..<numJoysticks():
      discard openJoystick(i)

    scene.joy = 0

  # somewhere in the update method

    let move = 100 * elapsed / JoyAxis.high.float
    scene.player.pos.x += scene.joy.joyAxis(0).float * move
    scene.player.pos.y += scene.joy.joyAxis(1).float * move

    # the button was pressed
    if scene.joy.joyPressed(0): scene.player.startCharging()
    # the button was released
    if scene.joy.joyReleased(0): scene.player.discharge()

Sprite Initialization

  # somewhere in the entity initialization routine

    # assign the graphic
    entity.graphic = graphic
    # set the sprite dimensions and offset
    # (if the graphic has a border)
    entity.initSprite((24, 48), offset = (16, 16))

    # add animations
    discard scene.d.addAnimation(
      "run_right", [0, 1, 2, 3])
    discard scene.d.addAnimation(
      "run_left", [0, 1, 2, 3], flip = Flip.horizontal)

    # or, if your frames are in order,
    # use toSeq() from types.nim
    discard scene.d.addAnimation(
      "jump_right", toSeq(4..7))
    discard scene.d.addAnimation(
      "jump_left", toSeq(4..7), flip = Flip.horizontal)

Collider Initialization

  # somewhere in the entity initialization routine

    entity.collider = newBoxCollider(entity, (0, 0), entity.graphic.dim)

TextGraphic Usage

  # somewhere in the scene initialization routine

    # BitmapFont

    # create a bitmap font
    scene.bitmapFont = newBitmapFont()
    # load the font file, giving the dimensions of a single character
    discard scene.bitmapFont.load("data/font/bitmap.png", (8, 16))
    # create a text graphic
    scene.bitmapText = newTextGraphic()
    # assign the font
    scene.bitmapText.font = scene.bitmapFont
    # set the text
    scene.bitmapText.lines =
      [ "Text line 1",
        "Text line 2"]
    # create an entity
    scene.bitmapEntity = newEntity()
    # assign the graphic
    scene.bitmapEntity.graphic = scene.bitmapText
    # add to the scene
    scene.add(scene.bitmapEntity)

    # TrueTypeFont

    # create a TrueType font
    scene.trueTypeFont = newTrueTypeFont()
    # load the font file, giving the font size
    discard scene.trueTypeFont.load("data/font/truetype.ttf", 16)
    # create a text graphic
    scene.trueTypeText = newTextGraphic()
    # assign the font
    scene.trueTypeText.font = scene.trueTypeFont
    # set the text
    scene.trueTypeText.lines =
      [ "Text line 1",
        "Text line 2"]
    # create an entity
    scene.trueTypeEntity = newEntity()
    # assign the graphic
    scene.trueTypeEntity.graphic = scene.trueTypeText
    # add to the scene
    scene.add(scene.trueTypeEntity)

ProcGraphic Usage

  # somewhere in the initialization routine

  proc customProc(graphic: ProcGraphic,
                  pos: Coord,
                  angle: Angle,
                  scale: Scale,
                  center: Coord,
                  flip: Flip,
                  region: Rect) =
    # your custom drawing routine goes here


  proc init*(scene: MyScene) =
    Scene(scene).init()
    # create a ProcGraphic
    scene.customGraphic = newProcGraphic()
    # assign the procedure
    scene.customGraphic.procedure = customProc
    # create an entity
    scene.myEntity = newEntity
    # assign the graphic
    scene.myEntity.graphic = scene.customGraphic
    # add to the scene
    scene.add(scene.myEntity)

IndexedImage Usage

# somewhere in the initialization routine

  let myImage = newIndexedImage("data/gfx/unit.gif")
  var color = myImage.palette[8] # get palette color
  color.r = 255
  color.g = 0
  color.b = 0
  myImage.palette[8] = color # set palette color

  # generate graphic
  let myGraphic = newTextureGraphic()
  discard myGraphic.assignTexture myImage.render()

PerspectiveImage Usage

# somewhere in the initialization routine

  let myImage = newPerspectiveImage("data/gfx/pimage.png")
  let myGraphic = newTextureGraphic()
  # render with distortion
  myGraphic.assignTexture myImage.render(pdHor, 64, 32)
  let myEntity = newEntity()
  myEntith.graphic = myGrapghic
  scene.add(myEntity)

TextureAtlas Usage

# somewhere in the initialization routine

  let myAtlas = newTextureAtlas("data/gfx/atlas.png", "data/csv/atlas.csv")
  entity1.graphic = myAtlas["image1"]
  entity2.graphic = myAtlas["image2"]

Tween Usage

  let tween = newTween[Entity,Coord](
    target = myEntity,
    get = proc(t: Entity): Coord = t.pos,
    set = proc(t: Entity, val: Coord) = t.pos = val
  )
  tween.procedure = inQuad
  tween.setup(
    start = myEnttiy.pos,
    finish = myEntityPos + (100.0, 0.0),
    duration = 3.0,
    loops = -1
  )
  tween.play()

Emitter Usage

  # somewhere in the initialization routine
    # init emitter
    myEmitter = newEmitter(myScene)
    myEmitter.randomVel = (10.0, 10.0)
    myEmitter.randomAcc = (5.0, 5.0)
    myEmitter.randomTTL = 5.0
    myScene.add(myEmitter)
    # init emitter particle
    myEmitter.particle = newParticle()
    myEmitter.particle.graphic = myParticleGraphic
    myEmitter.particle.initSprite((5, 5))
    myEmitter.particle.centrify()
    discard myEmitter.particle.addAnimation("play", toSeq(0..4), 1/5)
    myEmitter.particle.play("play", 1, kill = true)

  # somewhere in the update routine
    if emitCondition:
      myEmitter.emit(5) # emit 5 particles

TileMap Usage

  # somewhere in the initialization routine

    # tiles
    scene.tilesGraphic = newTextureGraphic()
    discard scene.tilesGraphic.load("data/gfx/tile.png")

    # map
    scene.map = newTileMap(scaleFix = true)
    scene.map.graphic = scene.tilesGraphic
    scene.map.initSprite((64, 64), offset = (0, 0))
    scene.map.map = loadCSV[int](
        "data/csv/map.csv",
        proc(input: string): int = discard parseInt(input, result)
    )
    scene.map.passable.add(0)
    scene.add(scene.map)

TileMap Collision List

  # checking specific tile collisions
  let
    collider = TilemapCollider(map.collider)
    collisions = collider.collisionList(myEntity.collider)
    for tile in collisions:
      echo
        "Collided with " & $tile.value & " at " & $tile.mapx & ":" & $tile.mapy

Camera Usage

  # somewhere in the initialization routine

    scene.camera = newEntity()

    scene.map = newMyMap()
    scene.map.parent = scene.camera # this entity is bound to the camera
    scene.add(scene.map)

    scene.player = newMyPlayer()
    scene.player.parent = scene.map # this entity is bound to the map
    scene.add(scene.player)

    # this entity isn't bound to the camera
    scene.ui = newUI()
    scene.add(scene.ui)

    # boud camera to this entity
    scene.cameraBond = scene.player
    # place camera bond entity at the center
    scene.cameraBondOffset = game.size / 2

  # somewhere in the update routine

    if scene.cameraBond == nil:
        if ScancodeRight.down: scene.camera.pos.x += step
        # ...
    else:
        if ScancodeRight.down: scene.player.pos.x += step
        # ...

Sound Usage

  # somewhere in the initialization routine

    # load a sound file
    scene.soundEffect = newSound("data/sfx/effect.wav")
    # set the volume
    scene.soundEffect.volume = Volume.high div 2

  # somewhere else

    # the call
    scene.soundEffect.play()

Music Playlist Usage

  # somewhere in the initialization routine

    # load all music files
    var musicData: Assets[Music]
    musicData = newAssets[Music]("data/music",
                                 proc(file: string): Music = newMusic(file))
    # global playlist
    playlist = newPlaylist()
    # fill the playlist
    for track in musicData.values:
      playlist.list.add(track)

  # somewhere else

    # next random track
    discard playlist.play()

  # somewhere in the update routine

    playlist.update()

GUI: Buttons

# mybutton.nim

import
  nimgame2 / [
    graphic,
    input,
    gui/button,
  ]

type
  MyButton* = ref object of GuiButton

proc init*(btn: MyButton, graphic: Graphic, image: Graphic = nil) =
  GuiButton(btn).init(graphic, image)

proc newMyButton*(graphic: Graphic, image: Graphic = nil): MyButton =
  new result
  result.init(graphic, image)

method onClick*(btn: MyButton, mb: MouseButton) =
  echo "clicked my button"


# somewhere in the initialization routine
  # skin loading
  let mySkin = newTextureGraphic()
  discard mySkin.load("data/gfx/button_skin.png")

  # button initialization
  myBtn = newMyButton(mySkin)
  scene.add(myBtn)

GUI: Actions

# action procedure
proc myBtnAction(widget: GuiWidget) =
  echo "clicked my button"

# somewhere in the initialization routine
  # skin loading
  let mySkin = newTextureGraphic()
  discard mySkin.load("data/gfx/button_skin.png")

  # button initialization
  myBtn = newGuiButton(mySkin)
  myBtn.action = myBtnAction
  scene.add(myBtn)

GUI: Text Input (+ Mosaic)

import
  nimgame2 / [
    scene,
    mosaic,
    texturegraphic,
    truetypefont,
    gui/widget,
    gui/textinput,
  ]

# somewhere in the initialization routine

  # mosaic creation
  let myMosaic = newMosaic("data/gfx/textinput_skin.png")
  let mySkin = newTextureGraphic()
  discard mySkin.assignTexture myMosaic.render(patternStretchBorder(16, 1))

  # text input initialization
  myTextInput = newGuiTextInput(mySkin, myFont)
  myTextInput.text.limit = 16 # set text length limit
  scene.add(myTextInput)

GUI: Radio Groups

import
  nimgame2 / [
    scene,
    texturegraphic,
    truetypefont,
    gui/widget,
    gui/radio,
  ]

type
  MyScene = ref object of Scene
    radioButtons: array[3, GuiRadioButton]
    radioGroup: GuiRadioGroup

# somewhere in the initialization routine

  scene.radioGroup = newGuiRadioGroup() # create group

  # init each button
  for i in 0..scene.radioButtons.high:
    scene.radioButtons[i] = newGuiRadioButton(scene.radioGroup, scene.mySkin)

  scene.radioButtons[0].toggled = true # toggle the first button

  # add to the scene
  scene.add(scene.radioGroup)
  for btn in scene.radioButtons:
    scene.add(btn)

GUI: ProgressBar

# somewhere in the initialization routine
  scene.bar = newProgressBar((200, 50), 0xFF0000FF'u32, 0xFF0000FF'u32, myFont)
  scene.bar.min = 0
  scene.bar.max = 100
  scene.bar.direction = Direction.leftRight
  scene.bar.outline = (1, 1) # one pixel outline
  scene.add(scene.bar)

# on update
  scene.bar.value = newValue