Skip to content

Commit ea18356

Browse files
shai-almogclaude
andcommitted
Polish gaming samples and expand the Game Development guide
- CasualGameSample: a juicy top-down arena -- thruster-flame ship, drifting asteroids, spinning gems, particle bursts, twinkling starfield -- with a safe-area anchored joystick. - ScrollerGameSample: an animated runner (a six-frame walk cycle that flips to face its direction), a sun/cloud/hill parallax backdrop, jump on the stick-up or a JUMP button, and a world that re-lays-out on rotation. - Gaming3DDemoSample: a spinning multi-cube monument on a textured grid ground, a ring of colored building Models scaled from one cube mesh, billboarded coins and trees, lit and orbited by a perspective camera. - Game Development guide: add a three-sample showcase with screenshots, a new On-screen touch controls section (joystick/buttons + safe-area anchoring), a sprite-sheet walk-cycle illustration, a 3D screenshot, a Meshes and textures section, and a build-recipe "anatomy of a game". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 34d8f19 commit ea18356

10 files changed

Lines changed: 865 additions & 134 deletions

File tree

CodenameOne/src/com/codename1/gaming/VirtualJoystick.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ private void moveTo(float px, float py) {
143143
if (dist > radius && dist > 0) {
144144
dx = dx / dist * radius;
145145
dy = dy / dist * radius;
146-
dist = radius;
147146
}
148147
knobX = centerX + dx;
149148
knobY = centerY + dy;

Samples/samples/CasualGameSample/CasualGameSample.java

Lines changed: 289 additions & 47 deletions
Large diffs are not rendered by default.

Samples/samples/Gaming3DDemoSample/Gaming3DDemoSample.java

Lines changed: 109 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import com.codename1.gaming.GameView;
55
import com.codename1.gaming.Model;
66
import com.codename1.gaming.Sprite;
7+
import com.codename1.gaming.TouchControls;
78
import com.codename1.gpu.GraphicsDevice;
89
import com.codename1.gpu.Material;
910
import com.codename1.gpu.Mesh;
1011
import com.codename1.gpu.Primitives;
12+
import com.codename1.gpu.Texture;
1113
import com.codename1.io.Log;
1214
import com.codename1.ui.Display;
1315
import com.codename1.ui.Form;
@@ -56,70 +58,114 @@ public void destroy() {
5658

5759
/// The 3D game surface.
5860
static class World3DView extends GameView {
59-
private static final int COIN_COUNT = 8;
60-
private static final float COIN_RING = 4f;
61+
private static final int COIN_COUNT = 10;
62+
private static final float COIN_RING = 5f;
63+
private static final int BUILDINGS = 8;
6164

62-
private Model cube;
65+
private final Model[] monument = new Model[3];
66+
private final Sprite[] coins = new Sprite[COIN_COUNT];
6367
private double time;
6468
private double orbit;
65-
private float camHeight = 6f;
69+
private float camHeight = 7f;
6670
private boolean controlsReady;
6771

6872
World3DView() {
69-
setClearColor(0xff101824);
70-
// perspective camera looking at the cube from above and behind
73+
setClearColor(0xff1a2a4a); // dusk sky
7174
getCamera()
72-
.setPerspective(60f, 0.1f, 200f)
73-
.setPosition(0f, 6f, 12f)
74-
.setTarget(0f, 1f, 0f);
75+
.setPerspective(60f, 0.1f, 300f)
76+
.setPosition(0f, 7f, 15f)
77+
.setTarget(0f, 2.5f, 0f);
7578

76-
// a ring of billboarded coins in 3D world space
79+
// a ring of billboarded coins floating in 3D world space
7780
Image coinImage = makeCoin(48, 0xffffd54a);
7881
for (int i = 0; i < COIN_COUNT; i++) {
7982
double a = i * 2 * Math.PI / COIN_COUNT;
80-
Sprite coin = new Sprite(coinImage);
81-
coin.setPosition(Math.cos(a) * COIN_RING, 1.2, Math.sin(a) * COIN_RING);
82-
coin.setSize(1.2f, 1.2f); // world units, not pixels
83-
getScene().add(coin);
83+
coins[i] = new Sprite(coinImage);
84+
coins[i].setPosition(Math.cos(a) * COIN_RING, 1.4, Math.sin(a) * COIN_RING);
85+
coins[i].setSize(1.1f, 1.1f); // world units, not pixels
86+
getScene().add(coins[i]);
87+
}
88+
// a few billboarded trees for scenery (always face the camera)
89+
Image treeImage = makeTree(72);
90+
double[] tx = {-11, 10, -9, 12, 2};
91+
double[] tz = {-10, -8, 9, 7, -13};
92+
for (int i = 0; i < tx.length; i++) {
93+
Sprite tree = new Sprite(treeImage);
94+
tree.setPosition(tx[i], 1.8, tz[i]);
95+
tree.setSize(3.6f, 3.6f);
96+
getScene().add(tree);
8497
}
8598
}
8699

87100
protected void onSetup(GraphicsDevice device) {
88-
// ground plane: a quad rotated flat, lit and green
89-
Mesh quad = Primitives.quad(device, 16f);
90-
Material grass = new Material(Material.Type.LAMBERT).setColor(0xff2f7d32);
91-
Model ground = new Model(quad, grass);
101+
// textured grid ground: a quad rotated flat, lit and tinted green
102+
Mesh groundMesh = Primitives.quad(device, 64f);
103+
Texture grid = device.createTexture(makeGrid(256, 0xffe8efe8, 0xff9fc0a0, 8));
104+
Material groundMat = new Material(Material.Type.LAMBERT)
105+
.setColor(0xff3f7d4f).setTexture(grid);
106+
Model ground = new Model(groundMesh, groundMat);
92107
ground.setRotation(-90f, 0f, 0f); // XY quad -> horizontal XZ ground
93108
addModel(ground);
94109

95-
// a gold spinning cube sitting on the ground
96-
Mesh cubeMesh = Primitives.cube(device, 2f);
97-
Material gold = new Material(Material.Type.PHONG).setColor(0xffffcc33).setShininess(32f);
98-
cube = new Model(cubeMesh, gold);
99-
cube.setPosition(0f, 1f, 0f);
100-
addModel(cube);
110+
// central spinning monument: three stacked, shiny cubes
111+
Mesh cubeMesh = Primitives.cube(device, 1f);
112+
int[] mcol = {0xffffcc33, 0xff33c7c7, 0xffff7043};
113+
float[] msize = {2.6f, 1.7f, 1.0f};
114+
float y = 0f;
115+
for (int i = 0; i < 3; i++) {
116+
y += msize[i] / 2f;
117+
Material m = new Material(Material.Type.PHONG).setColor(mcol[i]).setShininess(24f);
118+
Model c = new Model(cubeMesh, m);
119+
c.setScale(msize[i]);
120+
c.setPosition(0f, y, 0f);
121+
monument[i] = c;
122+
addModel(c);
123+
y += msize[i] / 2f;
124+
}
125+
126+
// a ring of colored "buildings" of varying height (one cube mesh, scaled)
127+
int[] bcol = {0xff5b8def, 0xffe06ca0, 0xff8bd450, 0xfff2b134,
128+
0xff9b6cf0, 0xff4fd1c5, 0xffef6f6f, 0xffb0bec5};
129+
for (int i = 0; i < BUILDINGS; i++) {
130+
double a = i * 2 * Math.PI / BUILDINGS;
131+
float h = 1.6f + (i % 4) * 0.9f;
132+
Material m = new Material(Material.Type.LAMBERT).setColor(bcol[i % bcol.length]);
133+
Model b = new Model(cubeMesh, m);
134+
b.setScale(1.5f, h, 1.5f);
135+
b.setPosition((float) (Math.cos(a) * 9.5), h / 2f, (float) (Math.sin(a) * 9.5));
136+
addModel(b);
137+
}
101138

102-
getLight().setDirection(-0.5f, -1f, -0.4f).setColor(0xffffffff).setAmbientColor(0xff404048);
139+
getLight().setDirection(-0.4f, -1f, -0.3f).setColor(0xfffff4e0)
140+
.setAmbientColor(0xff3a4257);
103141
}
104142

105143
protected void update(double dt) {
106144
time += dt;
107-
if (cube != null) {
108-
cube.setRotation(0f, (float) (time * 60), 0f); // spin around Y
145+
// each monument tier spins at its own speed and direction
146+
for (int i = 0; i < monument.length; i++) {
147+
if (monument[i] != null) {
148+
float dir = (i % 2 == 0) ? 1f : -1f;
149+
monument[i].setRotation(0f, (float) (time * (30 + i * 22)) * dir, 0f);
150+
}
151+
}
152+
// bob the coins up and down
153+
for (int i = 0; i < COIN_COUNT; i++) {
154+
coins[i].setY(1.4 + Math.sin(time * 2 + i) * 0.3);
109155
}
110156
if (!controlsReady && getWidth() > 0) {
111-
getControls().addJoystick(110, getHeight() - 110, 80);
157+
getControls().addJoystick(80, TouchControls.LEFT, TouchControls.BOTTOM, 30);
112158
controlsReady = true;
113159
}
114160
// the joystick orbits the camera (x) and raises/lowers it (y); when
115-
// untouched the camera drifts on its own.
161+
// untouched the camera drifts slowly on its own.
116162
float ax = getInput().getAxisX();
117163
float ay = getInput().getAxisY();
118-
orbit += (ax != 0 ? ax * 1.5 : 0.3) * dt;
119-
camHeight = clamp(camHeight + ay * 6f * (float) dt, 1.5f, 14f);
120-
float r = 12f;
164+
orbit += (ax != 0 ? ax * 1.5 : 0.25) * dt;
165+
camHeight = clamp(camHeight + ay * 7f * (float) dt, 2.5f, 18f);
166+
float r = 15f;
121167
getCamera().setPosition((float) (Math.sin(orbit) * r), camHeight,
122-
(float) (Math.cos(orbit) * r));
168+
(float) (Math.cos(orbit) * r)).setTarget(0f, 2.5f, 0f);
123169
}
124170

125171
private static float clamp(float v, float lo, float hi) {
@@ -138,4 +184,34 @@ static Image makeCoin(int size, int color) {
138184
g.fillArc(size / 4, size / 5, size / 3, size / 3, 0, 360); // highlight
139185
return img;
140186
}
187+
188+
/// Builds a grid texture (a base fill crossed by `cells` evenly spaced lines each
189+
/// way) used to tint and detail the ground so the 3D space reads with depth.
190+
static Image makeGrid(int size, int baseColor, int lineColor, int cells) {
191+
Image img = Image.createImage(size, size, baseColor);
192+
Graphics g = img.getGraphics();
193+
g.setColor(lineColor);
194+
int step = size / cells;
195+
for (int i = 0; i <= cells; i++) {
196+
int p = Math.min(size - 2, i * step);
197+
g.fillRect(p, 0, 2, size); // vertical line
198+
g.fillRect(0, p, size, 2); // horizontal line
199+
}
200+
return img;
201+
}
202+
203+
/// Builds a simple billboard tree (brown trunk, layered green canopy) on a
204+
/// transparent background.
205+
static Image makeTree(int size) {
206+
Image img = Image.createImage(size, size, 0);
207+
Graphics g = img.getGraphics();
208+
g.setAntiAliased(true);
209+
g.setColor(0xff7a4a26);
210+
g.fillRect(size / 2 - size / 14, size * 3 / 5, size / 7, size * 2 / 5); // trunk
211+
g.setColor(0xff2f8f3f);
212+
g.fillArc(size / 6, size / 5, size * 2 / 3, size * 2 / 3, 0, 360); // canopy
213+
g.setColor(0xff3fa84f);
214+
g.fillArc(size / 4, size / 8, size / 2, size / 2, 0, 360); // highlight
215+
return img;
216+
}
141217
}

0 commit comments

Comments
 (0)