|
| 1 | +// sprite demo: window, audio, camera, font, tiled, render, fx, spritesheet, input, ui. @todo: finish spine |
| 2 | +// - rlyeh, public domain. |
| 3 | +// |
| 4 | +// Compile with: |
| 5 | +// `make demos\01-sprite.c` (windows) |
| 6 | +// `sh MAKE.bat demos/01-sprite.c` (linux, osx) |
| 7 | + |
| 8 | +#include "fwk.h" |
| 9 | + |
| 10 | +void demo_kids(vec3 offs) { |
| 11 | + // init |
| 12 | + static texture_t kids; do_once kids = texture( "spriteSheetExample.png", TEXTURE_LINEAR ); |
| 13 | + static vec3 pos[2] = {{490,362},{442,362}}, vel[2] = {0}; |
| 14 | + static int row[2] = {0,3}, frame[2] = {0}; |
| 15 | + static int inputs[2][4] = {{KEY_W,KEY_A,KEY_S,KEY_D},{KEY_UP,KEY_LEFT,KEY_DOWN,KEY_RIGHT}}; |
| 16 | + |
| 17 | + // move |
| 18 | + for( int i = 0; i < countof(pos); ++i ) { |
| 19 | + vel[i].x = input(inputs[i][3]) - input(inputs[i][1]); |
| 20 | + vel[i].y = input(inputs[i][2]) - input(inputs[i][0]); |
| 21 | + pos[i].x = fmod(pos[i].x + vel[i].x, window_width() + 128); |
| 22 | + pos[i].y = fmod(pos[i].y + vel[i].y, window_height() + 128); |
| 23 | + frame[i] += vel[i].x || vel[i].y; |
| 24 | + } |
| 25 | + |
| 26 | + // render |
| 27 | + for( int i = 0; i < countof(pos); ++i ) { |
| 28 | + int col = frame[i] / 10, num_frame = row[i] * 4 + col % 4; // 4x4 tilesheet |
| 29 | + float position[3] = {pos[i].x,pos[i].y,pos[i].y}, offset[2]={0,0}, scale[2]={0.5,0.5}; |
| 30 | + float spritesheet[3]={num_frame,4,4}; |
| 31 | + sprite_sheet(kids, |
| 32 | + spritesheet, // num_frame in a 4x4 spritesheet |
| 33 | + position, 0, // position(x,y,depth:sort-by-Y), angle |
| 34 | + offset, scale, // offset(x,y), scale(x,y) |
| 35 | + false, WHITE, false // is_additive, tint color, resolution-independent |
| 36 | + ); |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +void demo_hud() { |
| 41 | + // draw pixel-art hud, 16x16 ui element, scaled and positioned in resolution-independant way |
| 42 | + static texture_t inputs; do_once inputs = texture( "prompts_tilemap_34x24_16x16x1.png", TEXTURE_LINEAR ); |
| 43 | + float spritesheet[3] = {17,34,24}, offset[2] = {0, - 2*absf(sin(window_time()*5))}; // sprite cell and animation |
| 44 | + float scale[2] = {3, 3}, tile_w = 16 * scale[0], tile_h = 16 * scale[1]; // scaling |
| 45 | + float position[3] = {window_width() - tile_w, window_height() - tile_h, window_height() }; // position in screen-coordinates (x,y,z-index) |
| 46 | + sprite_sheet(inputs, spritesheet, position, 0/*deg*/, offset, scale, false, WHITE, 1); |
| 47 | +} |
| 48 | + |
| 49 | +int main() { |
| 50 | + // window (80% sized, MSAA x4 flag) |
| 51 | + window_create(80.0, WINDOW_MSAA4); |
| 52 | + window_title(__FILE__); |
| 53 | + |
| 54 | + // tiled map |
| 55 | + tiled_t tmx = tiled(vfs_read("castle.tmx")); |
| 56 | + // tmx.parallax = true; |
| 57 | + |
| 58 | + // spine model |
| 59 | + //spine_t *spn = spine("goblins.json", "goblins.atlas", 0); |
| 60 | + |
| 61 | + // camera 2d |
| 62 | + camera_t cam = camera(); |
| 63 | + cam.position = vec3(window_width()/2, window_height()/2, 3); // at(CX,CY) zoom(x3) |
| 64 | + camera_enable(&cam); |
| 65 | + |
| 66 | + // audio (both clips & streams) |
| 67 | + audio_t clip1 = audio_clip( "coin.wav" ); |
| 68 | + audio_t clip2 = audio_clip( "pew.sfxr" ); |
| 69 | + audio_t stream1 = audio_stream( "larry.mid" ); |
| 70 | + audio_t stream2 = audio_stream( "monkey1.mid" ); |
| 71 | + audio_t BGM = stream1; |
| 72 | + audio_play(BGM, 0); |
| 73 | + |
| 74 | + // font config: faces (6 max) and colors (10 max) |
| 75 | + #define FONT_CJK FONT_FACE3 |
| 76 | + #define FONT_YELLOW FONT_COLOR2 |
| 77 | + #define FONT_LIME FONT_COLOR3 |
| 78 | + font_face(FONT_CJK, "mplus-1p-medium.ttf", 48.f, FONT_JP|FONT_2048); // CJK|FONT_2048|FONT_OVERSAMPLE_Y); |
| 79 | + font_color(FONT_YELLOW, RGB4(255,255,0,255)); |
| 80 | + font_color(FONT_LIME, RGB4(128,255,0,255)); |
| 81 | + |
| 82 | + // fx: load all post fx files in all subdirs. enable a few filters by default |
| 83 | + fx_load("fx**.fs"); |
| 84 | + fx_enable(fx_find("fxCRT2.fs"), 1); |
| 85 | + fx_enable(fx_find("fxGrain.fs"), 1); |
| 86 | + fx_enable(fx_find("fxContrast.fs"), 1); |
| 87 | + fx_enable(fx_find("fxVignette.fs"), 1); |
| 88 | + |
| 89 | + // demo loop |
| 90 | + while (window_swap() && !input_down(KEY_ESC)) { |
| 91 | + |
| 92 | + // handle input |
| 93 | + if( input_down(KEY_F5) ) window_reload(); |
| 94 | + if( input_down(KEY_F11) ) window_fullscreen( !window_has_fullscreen() ); |
| 95 | + |
| 96 | + // camera panning (x,y) & zooming (z) |
| 97 | + if( !ui_hover() && !ui_active() ) { |
| 98 | + if( input(MOUSE_L) ) cam.position.x += input_diff(MOUSE_X); |
| 99 | + if( input(MOUSE_L) ) cam.position.y += input_diff(MOUSE_Y); |
| 100 | + cam.position.z += input_diff(MOUSE_W) * 0.1; |
| 101 | + } |
| 102 | + |
| 103 | + // apply post-fxs from here |
| 104 | + fx_begin(); |
| 105 | + |
| 106 | + profile("Rendering") { |
| 107 | + vec3 center = add3(cam.position,vec3(-window_width()/1,-window_height()/2,0)); |
| 108 | + // render tiled map |
| 109 | + tiled_render(tmx, center); |
| 110 | + // |
| 111 | + demo_kids(vec3(0,0,1)); |
| 112 | + demo_hud(); |
| 113 | + // render spine model |
| 114 | + // spine_animate(spn, !window_has_pause() * window_delta()); |
| 115 | + // spine_render(spn, vec3(cam.position.x, cam.position.y, 1), true ); |
| 116 | + // sprite_flush(); |
| 117 | + } |
| 118 | + |
| 119 | + // subtitle sample |
| 120 | + font_print( |
| 121 | + FONT_BOTTOM FONT_CENTER |
| 122 | + FONT_CJK FONT_H1 |
| 123 | + FONT_YELLOW "私はガラスを食べられます。" FONT_LIME "それは私を傷つけません。\n" |
| 124 | + ); |
| 125 | + |
| 126 | + // post-fxs end here |
| 127 | + fx_end(); |
| 128 | + |
| 129 | + // ui |
| 130 | + if( ui_panel("Audio", 0)) { |
| 131 | + static float bgm = 1, sfx = 1, master = 1; |
| 132 | + if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm); |
| 133 | + if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx); |
| 134 | + if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master); |
| 135 | + if( ui_label2_toolbar("BGM: Leisure Suit Larry", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE); |
| 136 | + if( ui_label2_toolbar("BGM: Monkey Island", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE); |
| 137 | + if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0); |
| 138 | + if( ui_label2_toolbar("SFX: Pew", ICON_MD_VOLUME_UP)) audio_play(clip2, 0); |
| 139 | + ui_panel_end(); |
| 140 | + } |
| 141 | + if( ui_panel("Tiled", 0)) { |
| 142 | + ui_float("Zoom in", &cam.position.z); |
| 143 | + tiled_ui(&tmx); |
| 144 | + ui_panel_end(); |
| 145 | + } |
| 146 | + /*if( ui_panel("Spine", 0)) { |
| 147 | + spine_ui(spn); |
| 148 | + ui_panel_end(); |
| 149 | + }*/ |
| 150 | + if( ui_panel("FX", 0) ) { |
| 151 | + for( int i = 0; i < 64; ++i ) { |
| 152 | + char *name = fx_name(i); if( !name ) break; |
| 153 | + bool b = fx_enabled(i); |
| 154 | + if( ui_bool(name, &b) ) fx_enable(i, fx_enabled(i) ^ 1); |
| 155 | + } |
| 156 | + ui_panel_end(); |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +// this demo supersedes following old sources: |
| 162 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-audio.c |
| 163 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-font.c |
| 164 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-spine.c |
| 165 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-sprite.c |
| 166 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tiled.c |
| 167 | +// https://github.com/r-lyeh/FWK/blob/45e34d7890b2b8fe1f4994f4b76e496280d83cb6/demos/00-tilemap.c |
0 commit comments