ภาษาไทย: docs/TUTORIAL.th.md
A game engine for the J2K language, written in J2K. You make 2D games (a Terraria or a Stardew kind of game, a platformer, a puzzle) and, later, 3D with shaders; they run on a phone (Termux and the Jaric app) and on any Linux ARM64 machine, and they can be packed as an Android app.
Why it exists. One person, one phone, no borrowed tools: the language, the compiler, the libraries and the engine are all written in J2K. The engine is small and data-oriented: the world is tiles in chunks, a body is a few numbers, a picture is an array of dots. You read the whole engine in an evening.
What you have today
| you want | use | what it gives |
|---|---|---|
| a window, the loop, keys, touch, time, sound | <gamej> |
Game: begin() / end() for each frame, g.dt, held / hit / tapped, rectangles, lines, text, sound |
| pictures from files | <png> + <sprite> |
Sprites::load("hero.png"), draw, draw_part (sheets, turned), draw_scaled, cut, see-through dots |
| a world made of tiles | <tilemap> |
layers, chunks of 16 x 16 (a big empty world costs nothing), get / set / fill, dirty / clean, save / load |
| a character that walks, falls, jumps | <phys2d> |
Body.step (collides with the tiles), jump, Camera.follow / limit |
| the graphics chip | <gpu>, <opengl3>, <vulkan>, <native> |
batches of triangles, shaders, compute (see below) |
jget -li install deflate png sprite tilemap phys2d gamej # the libraries (jget: https://github.com/J2k-studio/Jcmp-Tools)
jget tool jcmp # the compiler, 1.2.1.0 or newer
makej init -game # a game project with the engine and five lessons
makej apk # the game as an Android app; makej pack = .jc; makej dist = every system
Tutorial: from a square to a mini Terraria (the files are in examples/tutorial/, each one builds and runs)
Run each step from the folder examples/tutorial (jcmp stepN_name.jk -o stepN && ./stepN). In a terminal the arrows move and Esc ends; GAME_FRAMES=30 ./stepN runs 30 frames without a terminal and ends (for tests).
Step 1: a window and a square (step1_square.jk). A game is a loop: g.begin() reads the keys and sets g.dt (the seconds since the last frame, so that the speed does not depend on the phone), you change the world, you draw, g.end() shows the picture and waits.
Game g = Game::open("Step 1");
double x = 20.0;
while g.running() {
g.begin();
if g.held(Key::left()) { x -= 40.0 * g.dt; }
if g.held(Key::right()) { x += 40.0 * g.dt; }
g.clear(Game::rgb(10, 20, 40));
g.rect((int)x, 30, 6, 6, Game::rgb(255, 200, 60));
g.end();
}
g.close();
The screen is a grid of dots (g.w x g.h); a colour is a number Game::rgb(r, g, b).
Step 2: a picture from a PNG (step2_sprite.jk, assets/hero.png). #import <deflate>, <png>, <sprite>, then Sprite hero = Sprites::load("assets/hero.png", @ok);. A sheet has several frames side by side: hero.draw_part(g, frame * 8, 0, 8, 10, x, y, flip) draws one frame (8 x 10 dots), turned left to right when flip is true; hero.draw_scaled(g, x, y, 3) makes it three times bigger with sharp dots. The dots may be see-through (alpha) or half see-through.
Step 3: a world of tiles and a camera (step3_world.jk). TileMap world = TileMap::make(200, 60, 1); is 200 x 60 tiles with one layer; a tile is a number (0 is empty, the rest means what you decide: here 1 grass, 2 dirt). world.set(0, x, y, 1), world.fill(0, x, y, w, h, 2), world.get(0, x, y). A Camera knows what part of the world is seen: cam.follow(x, y, 1.0) centres it, cam.screen_x(world_x) turns a place in the world into a place on the screen, cam.limit(world_w, world_h) keeps it inside. You draw only the tiles that are in the view.
Step 4: a body that falls and walks (step4_body.jk). Body p = Body::make(x, y, 3.0, 6.0); is a box with a speed. Set p.vx from the keys, call p.jump(75.0) (works only on the ground), then p.step(world, 0, 4, 99, g.dt, 160.0): the layer, the size of a tile in dots, the tiles 1 to 99 are solid, the time, the gravity. The body is put against the tile it meets; p.on_ground, p.hit_x, p.hit_y tell what it touched.
Step 5: dig and build, a mini Terraria (step5_mini_terraria.jk). The same parts and two more ideas (the example does the first; the second you add): world.set(0, tx, ty, 0) digs (the tile in front of the body: tx = (int)((p.x + p.w / 2.0) / 4.0) + face), world.set(0, tx, ty, 4) puts a block; world.dirty(0, x, y) tells which chunks changed (draw or save only those) and world.save("world.jw") / TileMap::load("world.jw", @ok) keep the world in a text file.
Step 6: make it an app. makej init -game makes a project (folders, a manifest, five lessons); makej apk packs it as an Android app, makej pack as a .jc package for Jaric OS (it shows on the home screen with the name of its manifest and runs when you tap it), makej dist for every system. A backend of <gamej> for the screen and touch calls of Jaric OS is the next step; today a game in a package draws with those calls by hand (see jaric-os examples draw.jk).
Where next. Fonts made of pictures, loading all the assets at once (Content), a scene of nodes (.jscn), an editor, 3D and shaders: the plan is in docs/engine-plan.md (stages E0 to E6).
| library | what it is | needs |
|---|---|---|
<gamej> |
the framework: the loop, input, time, 2D drawing, text, pictures made of letters, collision, sound, touch | draws in a terminal (half blocks, 24-bit colour) or on a surface in the Jaric app |
<gpu> |
the graphics chip through the Jaric app: triangles, rectangles, lines, textures, tint, alpha, one batch of commands per picture (OpenGL ES in the app) | the Jaric app |
<native> |
call the libraries of the system from J2K (dlopen, dlsym, C calls, float arguments, memory) |
a program built with jcmp -dyn |
<opengl3> |
OpenGL ES 3.1 / 2.0 on the real driver: shaders (GLSL), triangles, compute shaders, read the pixels back | jcmp -dyn and <native> |
<vulkan> |
Vulkan on the real driver (first step: instance, the chip, memory, queues); the rest is built with <native> |
jcmp -dyn and <native> |
jcmp -dyn examples/opengl3_triangle_and_compute.jk -o gl && ./gl # triangle + a compute shader on the Mali-G76
jcmp -dyn examples/vulkan_info.jk -o vk && ./vk # Mali-G76 Vulkan 1.1.97 ...
How it works: a program built with -dyn is started by the loader of Android and gets libdl.so; <native> finds dlopen/dlsym in memory and calls any function of libEGL.so, libGLESv3.so, libvulkan.so through function pointers. No app and no C code is needed.
Layers (like MonoGame below, like Godot above): 0 the chip (<gpu>, <opengl3>, <vulkan>), 1 the framework (<gamej>), 2 scenes and nodes (planned), 3 tools (editor, planned), 4 export (makej dist: Android, Jaric OS .jc, Linux). See docs/roadmap.md. Tutorial in the J2K repository: lessons 61 to 66.
License: MPL-2.0.