|
| 1 | +--- |
| 2 | +type: major |
| 3 | +--- |
| 4 | + |
| 5 | +Scratch for Java 5 is about what a beginner meets first. |
| 6 | + |
| 7 | +Version 4 grew by addition. Every capability arrived as more public methods on |
| 8 | +`Sprite` and `Stage`, more constructors, more public fields to assign before |
| 9 | +starting, and more packages to import. A learner opening `Sprite` in BlueJ met |
| 10 | +289 public methods, most of which had no Scratch block behind them, and a |
| 11 | +mistyped costume name closed BlueJ itself. |
| 12 | + |
| 13 | +v5 removes what nobody needed, moves what belonged elsewhere, and fills the |
| 14 | +gaps that were actually stopping projects. `Sprite` and `Stage` are down from |
| 15 | +**289 public methods to 193**, and everything a first course needs is behind one |
| 16 | +import. |
| 17 | + |
| 18 | +Nothing here is a rename for its own sake: every removal is either a second way |
| 19 | +to do something that already had a first way, or something with no Scratch |
| 20 | +equivalent and no demonstrated use. |
| 21 | + |
| 22 | +## Migrating from v4 |
| 23 | + |
| 24 | +### One import |
| 25 | + |
| 26 | +Eight packages moved into `org.openpatch.scratch`, so `Pen`, `Text`, `Timer`, |
| 27 | +`AnimatedSprite`, `Vector2`, `Random`, `Color`, `Hitbox` and the shapes all come |
| 28 | +from one line: |
| 29 | + |
| 30 | +```java |
| 31 | +import org.openpatch.scratch.*; |
| 32 | +``` |
| 33 | + |
| 34 | +Delete any `import org.openpatch.scratch.extensions.animation.*;` and its |
| 35 | +siblings for `color`, `hitbox`, `math`, `pen`, `shape`, `text` and `timer`. |
| 36 | + |
| 37 | +Five packages are still opt-in, for things a first course does not meet: |
| 38 | +`camera`, `fs`, `recorder`, `shader` and `tiled`, joined by the new `pixels` and |
| 39 | +`sorting`. |
| 40 | + |
| 41 | +### Handlers are overridden, not registered |
| 42 | + |
| 43 | +The whole `setWhenX` family is gone — 16 setters across `Sprite`, `Stage` and |
| 44 | +`Window`, and the 16 nested `When*Handler` interfaces that went with them. Every |
| 45 | +event already had an overridable method, which is the form that matches Scratch: |
| 46 | + |
| 47 | +```java |
| 48 | +// v4 |
| 49 | +this.setWhenKeyPressed((keyCode) -> { ... }); |
| 50 | + |
| 51 | +// v5 |
| 52 | +public void whenKeyPressed(KeyCode keyCode) { ... } |
| 53 | +``` |
| 54 | + |
| 55 | +`setRun` goes the same way — override `run()`. And `setOnEdgeBounce(true)` |
| 56 | +becomes a call inside `run()`, the way the block works: |
| 57 | + |
| 58 | +```java |
| 59 | +this.ifOnEdgeBounce(); |
| 60 | +``` |
| 61 | + |
| 62 | +### Settings are methods, chosen before the window opens |
| 63 | + |
| 64 | +Public fields you assigned before starting did nothing at all if you assigned |
| 65 | +them afterwards — no error, no warning. They are methods now, and they say so |
| 66 | +when used too late: |
| 67 | + |
| 68 | +| v4 | v5 | |
| 69 | +|---|---| |
| 70 | +| `new Stage(true, 800, 600)` | `Window.useFullScreen();` then `new Stage(800, 600)` | |
| 71 | +| `Window.TEXTURE_SAMPLING_MODE = 2;` | `Window.useTextureSampling(TextureSampling.POINT);` | |
| 72 | +| `Text.DEFAULT_FONT = "f.ttf";` | `Text.useFont("f.ttf");` | |
| 73 | +| `Text.DEFAULT_FONT_SIZE = 11;` | `Text.useFont("f.ttf", 11);` | |
| 74 | +| `Text.FONT_SIZES = new int[]{14, 20};` | `Text.useFontSizes(14, 20);` | |
| 75 | +| `Text.SMOOTHING = false;` | `Text.useSmoothing(false);` | |
| 76 | +| `Stage.setTextureSampling(...)` | `Window.useTextureSampling(...)` | |
| 77 | + |
| 78 | +`TextureSampling` and `TextAlign` are enums rather than loose ints, so a wrong |
| 79 | +value can no longer be written. Code that already said `TextAlign.LEFT` compiles |
| 80 | +unchanged. |
| 81 | + |
| 82 | +### Methods that moved to a class of their own |
| 83 | + |
| 84 | +Nine clock values and nine shader methods existed on **both** `Sprite` and |
| 85 | +`Stage` — 36 methods for 18 ideas. |
| 86 | + |
| 87 | +| v4 | v5 | |
| 88 | +|---|---| |
| 89 | +| `this.getCurrentYear()` | `Clock.getYear()` | |
| 90 | +| `this.addShader(...)`, `this.switchShader(...)` | `this.getShaders().add(...)`, `.switchTo(...)` | |
| 91 | +| `this.setSorter(...)`, `this.enableYSort()` | `this.getSorting().byY()` | |
| 92 | +| `this.getPixels()` → `int[]` | `this.getPixels().main()` → `int[]` | |
| 93 | +| `this.stampToUI()` | `this.stamp(Layer.UI)` | |
| 94 | +| `this.getWindow()` | `Window.getInstance()` | |
| 95 | +| `stage.countSpritesOf(C.class)` | `stage.count(C.class)` | |
| 96 | +| `stage.findPensOf(C.class)` | `stage.find(C.class)` | |
| 97 | + |
| 98 | +`Layer` is a new enum with `BACKGROUND`, `FOREGROUND` and `UI`, so choosing a |
| 99 | +layer is something you say rather than a method name you pick. |
| 100 | + |
| 101 | +### Overloads that made the wrong thing easy |
| 102 | + |
| 103 | +- `broadcast(Object)` and `whenIReceive(Object)` are gone. Broadcasting a |
| 104 | + non-`String` used to call only the `Object` handler, so a sprite overriding |
| 105 | + `whenIReceive(String)` silently heard nothing. Messages are names, as in |
| 106 | + Scratch. |
| 107 | +- `whenAddedToStage(Stage)` and `whenRemovedFromStage(Stage)` are gone — |
| 108 | + override the no-argument forms; the argument is what `getStage()` returns. |
| 109 | +- `setHitbox(Hitbox)` and `setHitbox(double[], double[])` are gone. |
| 110 | + `setHitbox(Shape)` and `setHitbox(double... points)` cover both. |
| 111 | + |
| 112 | +### Interface parts have their own class |
| 113 | + |
| 114 | +Buttons and bars want to be drawn on top, ignore the camera, and be sized in |
| 115 | +pixels rather than percent. That is now `UISprite`: |
| 116 | + |
| 117 | +```java |
| 118 | +public class Button extends UISprite { |
| 119 | + public Button() { |
| 120 | + this.addCostume("button", "ui/button.png"); |
| 121 | + this.setNineSlice(12, 24, 12, 24); |
| 122 | + this.setWidth(600); |
| 123 | + } |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +`setWidth`, `setHeight`, `setNineSlice` and `disableNineSlice` are `protected` on |
| 128 | +`Sprite` and public on `UISprite`; `changeWidth` and `changeHeight` are on |
| 129 | +`UISprite` only; `isUI(boolean)` became `protected setUI(boolean)`; and |
| 130 | +`Stage.goToUILayer` is gone. |
| 131 | + |
| 132 | +### Fewer constructors |
| 133 | + |
| 134 | +`Stage` had seven and `Window` eight, four of each differing only by a leading |
| 135 | +`boolean fullScreen`. `Stage` keeps `Stage()`, `Stage(width, height)` and |
| 136 | +`Stage(width, height, assets)`; `Window` keeps those three plus |
| 137 | +`Window(assets)`. Full screen is `Window.useFullScreen()`. |
| 138 | + |
| 139 | +### Plumbing that was public only to cross a package |
| 140 | + |
| 141 | +`Stage.draw`, `pre`, `keyEvent` and `mouseEvent` were public only so the render |
| 142 | +loop could reach them from another package. They are private now, reached |
| 143 | +through an internal interface the stage hands over. |
| 144 | + |
| 145 | +`Stage.stamp(Queue<Stamp>, Layer)` went the same way. It existed so `TiledMap` |
| 146 | +could put a map's tiles into the stage's stamp queues, which meant a method |
| 147 | +taking `internal.Stamp` — a type nobody outside the library can build — sat in |
| 148 | +autocomplete next to the real blocks. `TiledMap` now goes through an internal |
| 149 | +accessor, and the method is package-private. |
| 150 | + |
| 151 | +Neither was reachable in any useful way before, so no project has to change. |
| 152 | +`Sprite.stamp(Layer)` is still the stamp block. |
| 153 | + |
| 154 | +## Behaviour that changed without the code changing |
| 155 | + |
| 156 | +These need no edit, but a v4 project may look or behave differently: |
| 157 | + |
| 158 | +- **Hitboxes now wrap the painted pixels**, not the whole costume. Costumes are |
| 159 | + often drawn into a larger canvas — a standing pose in a costume tall enough to |
| 160 | + hold a jumping one — and a sprite used to collide with that empty space. |
| 161 | + Collisions now match what a player can see. Hitboxes set with `setHitbox(...)` |
| 162 | + are unaffected. |
| 163 | +- **A sprite with no costume is no longer invisible.** It draws a marked box and |
| 164 | + explains once on the console what to add, because drawing nothing looks exactly |
| 165 | + like a broken program. A sprite with a hitbox and no costume — an invisible |
| 166 | + wall — is left alone. |
| 167 | +- **Asset failures throw `ScratchException` instead of calling `System.exit`.** |
| 168 | + Ending the process took BlueJ's virtual machine with it, so a mistyped costume |
| 169 | + name used to close everything the student had open. |
| 170 | +- **The loading screen fades in and out**, which adds about half a second to |
| 171 | + starting a project. |
| 172 | + |
| 173 | +## What is new |
| 174 | + |
| 175 | +- **838 built-in sprites and 266 built-in sounds** from [kenney.nl](https://kenney.nl) |
| 176 | + ship inside the jar, so a first project needs no downloads at all: |
| 177 | + |
| 178 | + ```java |
| 179 | + this.addCostume("alienGreen_walk1"); |
| 180 | + this.addAnimation("walk", "alienGreen_walk%d", 2); |
| 181 | + this.addSound("footstep_carpet_000"); |
| 182 | + ``` |
| 183 | + |
| 184 | + Names are matched ignoring case, a wrong name suggests the closest matches |
| 185 | + instead of reporting a missing file, and anything with a file extension is |
| 186 | + still treated as a path — so existing projects are unaffected. Ogg Vorbis is |
| 187 | + now a supported sound format. |
| 188 | + |
| 189 | +- **`ask` and `answer`**, so a program can take typed input. Unlike Scratch it |
| 190 | + does not pause the script, so check `isAsking()` or wait for `getAnswer()` to |
| 191 | + change. |
| 192 | + |
| 193 | +- **`glide(seconds, x, y)`** with `isGliding()`, **`setVolume` / `changeVolume` / |
| 194 | + `getVolume`**, and **`Stage.waitUntil(condition)`**. |
| 195 | + |
| 196 | +- **Your own logo on the loading screen**, with `Window.useSplashLogo(...)`. |
| 197 | + |
| 198 | +- **Named timers create themselves** — `getTimer("countdown")` no longer has to |
| 199 | + be declared with `addTimer` first. |
| 200 | + |
| 201 | +`touching color` was attempted and dropped: it needs the whole screen read back |
| 202 | +off the graphics card every frame, which is too slow for the machines this |
| 203 | +library is meant for. `getPixels()` carries the same limitation, now written |
| 204 | +down in its documentation. |
| 205 | + |
| 206 | +## Fixes |
| 207 | + |
| 208 | +- `ifOnEdgeBounce()` no longer leaves a sprite stuck half outside the stage. It |
| 209 | + used to reposition using the size of the hitbox alone, which only works while |
| 210 | + the hitbox is centred on the sprite; it now nudges the sprite by however far |
| 211 | + its hitbox pokes over the border. |
| 212 | +- Setting a stage no longer makes the window flash. On a scaled or HiDPI screen |
| 213 | + the letterbox bars came out grey for one frame, because a render buffer's |
| 214 | + OpenGL framebuffer was being allocated after the canvas had already been |
| 215 | + painted black. |
| 216 | +- `GifRecorder.stop()` no longer truncates the last frame of a recording. |
| 217 | +- Ogg files below roughly 8 kB used to decode to silence with no error, which |
| 218 | + covers most short sound effects. |
| 219 | +- The shaded "all" jar merges `META-INF/services`, so the Ogg decoder survives |
| 220 | + shading. |
| 221 | +- Sprites can be constructed and tested without a running window. |
| 222 | + |
| 223 | +## Documentation |
| 224 | + |
| 225 | +The book is now seven tutorials, and every Java block on the hand-written pages |
| 226 | +is compiled by `mvn test`, so an example cannot quietly stop working — which had |
| 227 | +already happened twice. |
| 228 | + |
| 229 | +- **Your first program**, **Make it Walk**, **Catch the Coins**, **Red Light, |
| 230 | + Green Light**, **Guess the Number**, **Bouncing Hedgehog**, **Dodge the |
| 231 | + Rocks**. All but the hedgehog need no downloaded files, and each links a |
| 232 | + finished project to compare against. |
| 233 | +- **Browsable pages for the built-in sprites and sounds**, generated from the |
| 234 | + jar's own resources, searchable, with a click copying the `addCostume(...)` or |
| 235 | + `addSound(...)` line. |
| 236 | +- **The reference site shows the Scratch block beside the Java call** on 113 |
| 237 | + pages, and 108 pages now carry a worked example, against one before v5. 429 |
| 238 | + stale pages for methods that no longer exist were deleted. |
| 239 | +- `./scripts/run.sh` picks any of the 153 demos, finished projects and reference |
| 240 | + examples and runs it. |
0 commit comments