Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Monday, 6 July 2015

Sprites in Sniff (Pt 1)

Everyone loves games, and Scratch makes it really easy to build simple games. However when we built Sniff we wanted it to be more like a "real" programming language, that could be used to solve all sorts of problems - not just sprite based games.

But of course that doesn't mean that writing sprite based games isn't fun! As we're rounding out the support for Sniff, we've now added support for Sprites, so you can write Scratch like games. However Sprites aren't part of the language as they are in Scratch - they're devices. While this means the two systems aren't exactly alike, it makes the implementation a lot cleaner, and makes it possible to do things that would be very difficult in Scratch.

We'll be developing the documentation for this over the next few weeks, but here's a brief introduction (and you can check the examples in SNIFF/examples/Hosted/sprite).

make nativeFile device
make fileData string

make display window device

make spriteManager device
make spriteX number
make spriteY number
make spriteHit boolean
make spriteID boolean
make spriteValue boolean

when start
..tell spriteManager to "drawAll"
..wait 0.1 secs

We start by making a whole bunch of devices and variables. The SpriteManager is going to look after all of our sprites. We'll use it more in part 2 of this tutorial, but its most important user facing task at this stage is to handle the drawing. You can ask individual sprites to draw, but for simple games, we're just going to task the spriteManager to "drawAll". We can put this in a separate script and just let it run.

The sprite manager uses the display device (which currently has to be a window, but we hope to support other display types in the future), which in turn uses a nativeFile device.

Now lets make some sprites:

make background sprite device
make donut sprite device
make player sprite device

when start
.set fileData to "sea.bmp"
.tell background to "loadCostume"
.set fileData to "turtle.bmp"
.tell player to "loadCostume"
.set fileData to "donut.bmp"
.tell donut to "loadCostume"

We make three sprite devices, starting with the background - by default they get drawn in the order they're created, so background has to go first. We then load up some images to set the appearance of each sprite. Here we've only loaded up one costume per sprite, but you can add up to 8, just by calling loadCostume again. "setCostume" and "nextCostume" let you cycle through the different appearances.

.set spriteValue to 2
.tell player to "setCostume"

Sniff windows are fixed at 640x480, so we move the background image to the centre of the screen:

.set spriteX to 320
.set spriteY to 240
.tell background to "moveTo"

To move things around, we can use XY coordinates to set (moveTo) or adjust (moveBy) the players position). We can also find the players position using "getPosition".

Alternativly we can turtle style graphics, with "turnBy", "turnTo", "getRotation": and moveForward:

.set spriteValue to 90
.tell player to "turnBy"
.set spriteValue to 100
.tell player to "moveForward"

We can combine that with code to read from the keyboard to move our character around:

when start
..set specialKeypress to ""
..tell display to "getEvent"
..if specialKeypress = "left"
...set spriteValue to -10
...tell player to "turnBy
..if specialKeypress = "right"
...set spriteValue to 10
...tell player to "turnBy
..if specialKeypress = "up"
...set spriteValue to 10
...tell player to "moveForward
..wait 0.1 secs

Now we need to configure the collision detection:

.tell background to "setUnhittable"
.tell player to "setUnhittable"
.tell donut to "setHittable"

The background takes no real part in the game, so we set it to be unhittable. Similarly we're interested in the player hitting the donut, rather than the donut hitting the player, so we set the donut as hittable, and the player as unhittable.

..tell player to "checkTouching"
..if spriteHit
...change score by 1
...set spriteX to pick random 30 to 600
...set spriteY to pick random 30 to 440
...tell donut to "moveTo"
..wait 0.1 secs

We call "checkTouching" on a sprite to discover it its hit something hittable. If it has (in this case) it must be the donut, in which case we increase the score by 1, and move the donut to a new random location.

Using these basic sprite functions its possible to write the standard chase the target, Scratch type games in Sniff. In addition Sniff has a much more powerful way of dynamically creating sprites through the SpriteManager, so you can spawn new sprites as your game is running, rather than having to code them all at compile time. The two approaches play nice together, so you can migrate to the advanced API when you're ready, but we'll leave that till next time.

No comments:

Post a Comment