Shirakumo / trial

A fully-fledged Common Lisp game engine
https://shirakumo.github.io/trial
Other
1.03k stars 50 forks source link

Problem drawing animated-sprite-entity #66

Closed Ecsodikas closed 11 months ago

Ecsodikas commented 11 months ago

I created a player shader-entity which inherits from animated-sprite and I'd like to draw it on the screen. I was looking through the examples and the Kandria source code to look for examples but I couldn't find anything (maybe because I didn't know exactly what I was searching for in the first place) to help me solve my problem. I also looked into some Ludum Dare entries build with Trial but those were not compatible with the current version.

So basically I just want to draw a sprite on the screen at a certain position. I created an asset-pool and defined an asset with the sprite data. The sprite data .json is basically a 1:1 copy of the example from the documentation.

{
  "frames": [
    {
      // Position and size of the frame within the atlas. Bottom left oriented, Y-down
      "frame": { "x": 0, "y": 0, "w": 1, "h": 1 },
      // Size and offset of the frame within its padding. Bottom left oriented, Y-down
      "spriteSourceSize": { "x": 0, "y": 0, "w": 1, "h": 1 },
      // Total size of the frame including padding
      "sourceSize": { "w": 50, "h": 50 },
      // How long the frame should last, in milliseconds
      "duration": 500
    },
  ],
  "meta": {
    // Relative path to the sprite atlas
    "image": "test.png",
    // Size of the atlas image
    "size": { "w": 50, "h": 50 },
    // Array of animation clips
    "frameTags": [
      {
        "name": "Idle",
        // Starting frame, 1-indexed
        "from": 1,
        // Ending frame (inclusive upper bound)
        "to": 17,
        // Animation to play after this one, if any
        "next": "Idle",
        // Which frame to loop to
        "loop": 1
      }
    ]
  }
}
(define-pool characters)
(define-asset (characters test) sprite-data
    "path/to/data/characters/test.json")

(define-shader-entity player (animated-sprite)
  ((position :initform (vec 250 250))
   (sprite-data :initform (// 'characters 'test))))

(defmethod setup-scene ((main main) scene)
  (enter (make-instance 'player) scene)
  (enter (make-instance '2d-camera) scene)
  (enter (make-instance 'render-pass) scene))

Calling the (launch) function now results in an error:

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION TRIAL:TARGET (5)>
when called with arguments
  (NIL).
   [Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]

I followed the getting started guide with the rotating cube and everything worked well. When I remove the player instance from the scene setup everything works fine and I get a window and the game loop is running.

Is there a small 2D example project where I can look at the basics to help me get started?

I'm probably missing something pretty obvious here but I can't figure it out. Every help is appreciated.

Thanks in advance.

Shinmera commented 11 months ago

As I said in the chat:

Shinmera: it's hard to say what's going wrong without a full code sample, but my best guess would be that they used a camera that requires a target, but did not pass one. Shinmera: I improved the sidescroll-camera a bit and added a sprite example to prove it's working.

A backtrace would also usually help diagnose issues

Ecsodikas commented 11 months ago

Thanks for the quick response, I couldn't get the chat to show me the message history because I didn't register there, or at least that's what I am thinking.

The backtrace looks like this:

Backtrace:
 0: ((:METHOD NO-APPLICABLE-METHOD (T)) #<STANDARD-GENERIC-FUNCTION TRIAL:TARGET (5)> NIL) [fast-method]
 1: (SB-PCL::CALL-NO-APPLICABLE-METHOD #<STANDARD-GENERIC-FUNCTION TRIAL:TARGET (5)> (NIL))
 2: ((:METHOD BIND-TEXTURES :AFTER (TEXTURED-ENTITY)) #<PLAYER {100157B973}>) [fast-method]
 3: ((SB-PCL::EMF BIND-TEXTURES) #<unused argument> #<unused argument> #<PLAYER {100157B973}>)
 4: ((:METHOD RENDER-WITH :AROUND (PER-OBJECT-PASS RENDERABLE SHADER-PROGRAM)) #<RENDER-PASS {1001579933}> #<PLAYER {100157B973}> #<SHADER-PROGRAM  ALLOCATED {100159D623}>) [fast-method]
 5: ((:METHOD RENDER-FRAME (PER-OBJECT-PASS T)) #<RENDER-PASS {1001579933}> #((#<PLAYER {100157B973}> . #<SHADER-PROGRAM  ALLOCATED {100159D623}>))) [fast-method]
 6: ((:METHOD RENDER (PIPELINE T)) #<PIPELINED-SCENE {103FC06E53}> NIL) [fast-method]
 7: ((:METHOD RENDER (TRIAL:MAIN TRIAL:MAIN)) #<MAIN {103F8696A3}> #<MAIN {103F8696A3}>) [fast-method]
 8: ((:METHOD RENDER :AROUND (T DISPLAY)) #<MAIN {103F8696A3}> #<MAIN {103F8696A3}>) [fast-method]
 9: ((:METHOD RENDER-LOOP (RENDER-LOOP)) #<MAIN {103F8696A3}>) [fast-method]
10: ((:METHOD RENDER-LOOP :AROUND (DISPLAY)) #<MAIN {103F8696A3}>) [fast-method]
11: ((LAMBDA NIL :IN MAKE-THREAD))
12: ((LABELS BORDEAUX-THREADS::%BINDING-DEFAULT-SPECIALS-WRAPPER :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))
13: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
14: ((FLET "WITHOUT-INTERRUPTS-BODY-174" :IN SB-THREAD::RUN))
15: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
16: ((FLET "WITHOUT-INTERRUPTS-BODY-167" :IN SB-THREAD::RUN))
17: (SB-THREAD::RUN)
18: ("foreign function: call_into_lisp_")
19: ("foreign function: funcall1")

I'll look at the sidescroll-camera. Thank you!

Shinmera commented 11 months ago

There's a public log, too: https://irclog.tymoon.eu/tynet-lichat/shirakumo

Oh, that's interesting. Looks like your entity isn't getting a texture assigned. Did you actually create a sprite atlas to go with that json? The json alone only describes where things are in the atlas.

Ecsodikas commented 11 months ago

There's a public log, too: https://irclog.tymoon.eu/tynet-lichat/shirakumo

That's good to know, thanks.

In the meantime I played around a bit because it looked like your new example is doing not much different to my approach. The only thing different is the way the texture is assigned to the object.

I found out that

(define-asset (characters test) sprite-data
    "/absolute/path/to/test.json")

and

(define-asset (characters test) sprite-data
    #p"characters/test.json")

is giving different results.

After fixing it, I now get a different error:

Invalid index 1 for (SIMPLE-VECTOR 1), should be a non-negative integer below 1.
   [Condition of type SB-INT:INVALID-ARRAY-INDEX-ERROR]

Restarts:
 0: [ABORT] Don't handle #<TICK {10011C02F3}> in #<PLAYER :PLAYER>.
 1: [LEAVE] Leave #<PLAYER :PLAYER> from the loop.
 2: [SKIP-EVENT] Skip handling the event entirely.
 3: [ABORT] Don't handle #<TICK {10011C02F3}> in #<PIPELINED-SCENE {10014C23A3}>.
 4: [LEAVE] Leave #<PIPELINED-SCENE {10014C23A3}> from the loop.
 5: [DISCARD-EVENTS] Discard all remaining events and exit
 --more--

Backtrace:
 0: ((:METHOD HANDLE (TICK ANIMATED-SPRITE)) #<unavailable argument> #<unavailable argument>) [fast-method]
 1: ((:METHOD HANDLE :AROUND (EVENT T)) #<TICK {10011C02F3}> #<PLAYER :PLAYER>) [fast-method]
 2: ((:METHOD HANDLE (EVENT EVENT-LOOP)) #<TICK {10011C02F3}> #<PIPELINED-SCENE {10014C23A3}>) [fast-method]
 3: ((:METHOD HANDLE :AROUND (EVENT T)) #<TICK {10011C02F3}> #<PIPELINED-SCENE {10014C23A3}>) [fast-method]
 4: (TRIAL::MAP-QUEUE #<unavailable argument> #<TRIAL::QUEUE READ-INDEX: 4 WRITE-INDEX: 5 CAPACITY: 1024 {1001637B03}>)
 5: ((:METHOD PROCESS (EVENT-LOOP)) #<PIPELINED-SCENE {10014C23A3}>) [fast-method]
 6: ((SB-PCL::EMF UPDATE) #<unused argument> #<unused argument> #<MAIN {10014C22B3}> 0.0d0 0.01 0)
 7: ((:METHOD RENDER-LOOP (RENDER-LOOP)) #<MAIN {10014C22B3}>) [fast-method]
 8: ((:METHOD RENDER-LOOP :AROUND (DISPLAY)) #<MAIN {10014C22B3}>) [fast-method]
 9: ((LAMBDA NIL :IN MAKE-THREAD))
10: ((LABELS BORDEAUX-THREADS::%BINDING-DEFAULT-SPECIALS-WRAPPER :IN BORDEAUX-THREADS::BINDING-DEFAULT-SPECIALS))
11: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
12: ((FLET "WITHOUT-INTERRUPTS-BODY-174" :IN SB-THREAD::RUN))
13: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
14: ((FLET "WITHOUT-INTERRUPTS-BODY-167" :IN SB-THREAD::RUN))
15: (SB-THREAD::RUN)
16: ("foreign function: call_into_lisp_")
17: ("foreign function: funcall1")
 --more--

My exported sprite only has 1 animation frame, so it's basically just a picture. Maybe this is a problem?

Shinmera commented 11 months ago

your animation in the json is saying the animation goes from frame 1 to 17, but you only have one frame.

Ecsodikas commented 11 months ago

I got a bit confused with all my files. I cleaned up my directory and now I have a clean new slate, that's why the json did not match the images I have. I duplicated my character sprite now 3 times so I got 3 animation frames in my atlas. The json generated by aseprite looks like this:

{ "frames": {
   "Test 0.aseprite": {
    "frame": { "x": 0, "y": 0, "w": 40, "h": 48 },
    "rotated": false,
    "trimmed": true,
    "spriteSourceSize": { "x": 10, "y": 8, "w": 40, "h": 48 },
    "sourceSize": { "w": 64, "h": 64 },
    "duration": 100
   },
   "Test 1.aseprite": {
    "frame": { "x": 40, "y": 0, "w": 40, "h": 48 },
    "rotated": false,
    "trimmed": true,
    "spriteSourceSize": { "x": 10, "y": 8, "w": 40, "h": 48 },
    "sourceSize": { "w": 64, "h": 64 },
    "duration": 100
   },
   "Test 2.aseprite": {
    "frame": { "x": 80, "y": 0, "w": 40, "h": 48 },
    "rotated": false,
    "trimmed": true,
    "spriteSourceSize": { "x": 10, "y": 8, "w": 40, "h": 48 },
    "sourceSize": { "w": 64, "h": 64 },
    "duration": 100
   }
 },
 "meta": {
  "app": "http://www.aseprite.org/",
  "version": "1.3.2-x64",
  "image": "Test.png",
  "format": "RGBA8888",
  "size": { "w": 120, "h": 48 },
  "scale": "1",
  "frameTags": [
  ],
  "layers": [
   { "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
  ],
  "slices": [
  ]
 }
}
The value
  #<HASH-TABLE :TEST EQUAL :COUNT 3 {10028C6813}>
is not of type
  SEQUENCE
   [Condition of type TYPE-ERROR]

Restarts:
 0: [ABORT] Don't set up the scene, leaving it empty.
 1: [RETRY] Retry SLY mREPL evaluation request.
 2: [*ABORT] Return to SLY's top level.
 3: [ABORT] abort thread (#<THREAD tid=13229 "sly-channel-1-mrepl-remote-1" RUNNING {1001148093}>)

Backtrace:
 0: ((:METHOD GENERATE-RESOURCES (SPRITE-DATA PATHNAME)) #<SPRITE-DATA CHARACTERS/TEST> #P"/home/ecsodikas/Repositories/enight/data/characters/Test.json" :MIN-FILTER :NEAREST :MAG-FILTER :NEAREST) [fast-m..
 1: ((SB-PCL::EMF GENERATE-RESOURCES) #<unused argument> #<unused argument> #<SPRITE-DATA CHARACTERS/TEST> #P"/home/ecsodikas/Repositories/enight/data/characters/Test.json")
 2: ((:METHOD LOAD :AROUND (ASSET)) #<SPRITE-DATA CHARACTERS/TEST>) [fast-method]
 3: ((:METHOD STAGE (ASSET STAGING-AREA)) #<SPRITE-DATA CHARACTERS/TEST> #<STAGING-AREA {1002152BD3}>) [fast-method]
 4: ((SB-PCL::EMF STAGE) #<unused argument> #<unused argument> #<SPRITE-DATA CHARACTERS/TEST> #<STAGING-AREA {1002152BD3}>)
 5: ((:METHOD STAGE :AROUND (T STAGING-AREA)) #<SPRITE-DATA CHARACTERS/TEST> #<STAGING-AREA {1002152BD3}>) [fast-method]
 6: ((SB-PCL::EMF STAGE) #<unused argument> #<unused argument> #<PLACEHOLDER-RESOURCE CHARACTERS/TEST[TEXTURE]> #<STAGING-AREA {1002152BD3}>)
 7: ((:METHOD STAGE :AROUND (T STAGING-AREA)) #<PLACEHOLDER-RESOURCE CHARACTERS/TEST[TEXTURE]> #<STAGING-AREA {1002152BD3}>) [fast-method]
 8: ((SB-PCL::EMF STAGE) #<unused argument> #<unused argument> #<PLAYER :PLAYER> #<STAGING-AREA {1002152BD3}>)
 9: ((:METHOD STAGE :AROUND (T STAGING-AREA)) #<PLAYER :PLAYER> #<STAGING-AREA {1002152BD3}>) [fast-method]
10: ((:METHOD STAGE :AFTER (CONTAINER STAGING-AREA)) #<PIPELINED-SCENE {1002142213}> #<STAGING-AREA {1002152BD3}>) [fast-method]
11: ((SB-PCL::EMF STAGE) #<unused argument> #<unused argument> #<PIPELINED-SCENE {1002142213}> #<STAGING-AREA {1002152BD3}>)
12: ((:METHOD STAGE :AROUND (T STAGING-AREA)) #<PIPELINED-SCENE {1002142213}> #<STAGING-AREA {1002152BD3}>) [fast-method]
13: ((:METHOD COMMIT (T LOADER)) #<PIPELINED-SCENE {1002142213}> #<LOADER {1002142243}>) [fast-method]
14: ((:METHOD CHANGE-SCENE (TRIAL:MAIN SCENE)) #<MAIN {1002142123}> #<PIPELINED-SCENE {1002142213}> :OLD NIL) [fast-method]
15: ((:METHOD SETUP-RENDERING :AFTER (TRIAL:MAIN)) #<MAIN {1002142123}>) [fast-method]
16: ((SB-PCL::EMF SETUP-RENDERING) #<unused argument> #<unused argument> #<MAIN {1002142123}>)
17: ((:METHOD INITIALIZE-INSTANCE :AFTER (DISPLAY)) #<MAIN {1002142123}> :CONTEXT #<unused argument>) [fast-method]
18: ((SB-PCL::EMF INITIALIZE-INSTANCE) #<unused argument> #<unused argument> #<MAIN {1002142123}> :CLEAR-COLOR (VEC3 0.2 0.3 0.3))
19: ((:METHOD INITIALIZE-INSTANCE :AROUND (DISPLAY)) #<MAIN {1002142123}> :CLEAR-COLOR (VEC3 0.2 0.3 0.3)) [fast-method]
20: ((:METHOD MAKE-INSTANCE (CLASS)) #<STANDARD-CLASS EU.ECSODIKAS.ELDRITCH:MAIN> :CLEAR-COLOR (VEC3 0.2 0.3 0.3)) [fast-method]
21: (LAUNCH-WITH-CONTEXT #<unavailable argument> #<unavailable &MORE argument>)
22: ((FLET TRIAL::THUNK :IN TRIAL:LAUNCH))
23: ((:METHOD TRIAL:LAUNCH (SYMBOL)) MAIN) [fast-method]
24: (LAUNCH)
25: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LAUNCH) #<NULL-LEXENV>)
26: (EVAL (LAUNCH))
27: ((LAMBDA NIL :IN SLYNK-MREPL::MREPL-EVAL-1))
28: (SLYNK::CALL-WITH-RETRY-RESTART "Retry SLY mREPL evaluation request." #<FUNCTION (LAMBDA NIL :IN SLYNK-MREPL::MREPL-EVAL-1) {1002819F2B}>)
29: ((LAMBDA NIL :IN SLYNK-MREPL::MREPL-EVAL-1))
30: ((LAMBDA NIL :IN SLYNK::CALL-WITH-LISTENER))
31: (SLYNK::CALL-WITH-BINDINGS ((*PACKAGE* . #<PACKAGE "EU.ECSODIKAS.ELDRITCH">) (*DEFAULT-PATHNAME-DEFAULTS* . #P"/home/ecsodikas/Repositories/enight/") (*) (**) (***) (/ NIL) ...) #<FUNCTION (LAMBDA NIL..
32: (SLYNK-MREPL::MREPL-EVAL-1 #<SLYNK-MREPL::MREPL mrepl-1-1> "(launch)")
33: (SLYNK-MREPL::MREPL-EVAL #<SLYNK-MREPL::MREPL mrepl-1-1> "(launch)")
34: (SLYNK:PROCESS-REQUESTS NIL)
35: ((LAMBDA NIL :IN SLYNK::SPAWN-CHANNEL-THREAD))
36: ((LAMBDA NIL :IN SLYNK::SPAWN-CHANNEL-THREAD))
37: (SLYNK-SBCL::CALL-WITH-BREAK-HOOK #<FUNCTION SLYNK:SLYNK-DEBUGGER-HOOK> #<FUNCTION (LAMBDA NIL :IN SLYNK::SPAWN-CHANNEL-THREAD) {100814800B}>)
38: ((FLET SLYNK-BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/home/ecsodikas/.config/emacs/.local/straight/repos/sly/slynk/backend/sbcl.lisp") #<FUNCTION SLYNK:SLYNK-DEBUGGER-HOOK> #<FUNCTION (LAMBDA NIL :IN SLY..
39: ((LAMBDA NIL :IN SLYNK::CALL-WITH-LISTENER))
40: (SLYNK::CALL-WITH-BINDINGS ((*PACKAGE* . #<PACKAGE "EU.ECSODIKAS.ELDRITCH">) (*DEFAULT-PATHNAME-DEFAULTS* . #P"/home/ecsodikas/Repositories/enight/") (*) (**) (***) (/ NIL) ...) #<FUNCTION (LAMBDA NIL..
41: ((LAMBDA NIL :IN SLYNK::SPAWN-CHANNEL-THREAD))
42: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
43: ((FLET "WITHOUT-INTERRUPTS-BODY-174" :IN SB-THREAD::RUN))
44: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
45: ((FLET "WITHOUT-INTERRUPTS-BODY-167" :IN SB-THREAD::RUN))
46: (SB-THREAD::RUN)
47: ("foreign function: call_into_lisp_")
48: ("foreign function: funcall1")

I'm sorry for the probably stupid questions. :(

Shinmera commented 11 months ago

You exported in the wrong mode. It needs to be in --format json-array

Ecsodikas commented 11 months ago

Thanks for your help. Additional to the problem with the wrong format I also switched the // call to a call to asset. And now it works like a charm. Thank you very much for your support!

Shinmera commented 11 months ago

Sure, glad you got it working in the end!