-
Notifications
You must be signed in to change notification settings - Fork 246
Pressing keys and clicking mouse
In this part, we'll learn how to handle keyboard and mouse input from the player and utilize it to plant trees and move camera to look around the forest.
So far, we've learned how to create windows, load pictures and draw sprites, even move them. But we didn't have any control over them, except through the code. That's about to change right now. Let's start with this code, you should be able to understand every single line of it, by now.
package main
import (
"image"
"os"
_ "image/png"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"golang.org/x/image/colornames"
)
func loadPicture(path string) (pixel.Picture, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return pixel.PictureDataFromImage(img), nil
}
func run() {
cfg := pixelgl.WindowConfig{
Title: "Pixel Rocks!",
Bounds: pixel.R(0, 0, 1024, 768),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
if err != nil {
panic(err)
}
win.SetSmooth(true)
for !win.Closed() {
win.Clear(colornames.Whitesmoke)
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
Today, we'll be planting trees, but we'll get clever about it. Instead of having a separate PNG file for each type of tree, we're going to use this spritesheet.
As you can see, a spritesheet is a single image file that contains multiple other images in it. In our case, the spritesheet looks really tiny, because it's pixel art. Don't worry, we'll scale them up so they look a lot better.
Download the spritesheet to the directory of your program and let's go! First, we need to load the spritesheet.
win, err := pixelgl.NewWindow(cfg)
if err != nil {
panic(err)
}
win.SetSmooth(true)
spritesheet, err := loadPicture("trees.png")
if err != nil {
panic(err)
}
Now, let's just go ahead and draw one of the trees in the spritesheet. How do we do that? Remember, sprite constructor takes two arguments: the actual picture and a rectangle, which specifies, which portion (frame) from the picture we want to draw. So, it's fairly easy, since we know that each tree in the spritesheet is 32x32 pixels large.
Here, we create a sprite that draws the lower-left tree from the spritesheet and draw it to the center of the screen.
spritesheet, err := loadPicture("trees.png")
if err != nil {
panic(err)
}
tree := pixel.NewSprite(spritesheet, pixel.R(0, 0, 32, 32))
tree.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
for !win.Closed() {
win.Clear(colornames.Whitesmoke)
tree.Draw(win)
win.Update()
}
Let's run the code!
Oh sure, the tree it very tiny because we didn't scale it up. Let's fix that right away!
tree := pixel.NewSprite(spritesheet, pixel.R(0, 0, 32, 32))
tree.SetMatrix(pixel.IM.Scaled(0, 16).Moved(win.Bounds().Center()))
That's better, but it's not good either. The tree is really blurry. That's because we've told the window to draw pictures smoothly. When they get scaled up, they end up looking like this. When doing pixel art, this is far from appropriate. We need to disable it.
Just delete this line.
win.SetSmooth(true) // delete this
Let's run the code now!
Much better! We'd like to be able to draw all types of trees, not just this one. For that we'll create a slice of rectangles. Each rectangle in this slice will be the portion of one of the trees. Since each tree is 32x32 pixels and they're packed together as tightly as possible, it's very easy.
spritesheet, err := loadPicture("trees.png")
if err != nil {
panic(err)
}
var treesFrames []pixel.Rect
for x := spritesheet.Bounds().Min.X(); x < spritesheet.Bounds().Max.X(); x += 32 {
for y := spritesheet.Bounds().Min.Y(); y < spritesheet.Bounds().Max.Y(); y += 32 {
treesFrames = append(treesFrames, pixel.R(x, y, x+32, y+32))
}
}
Now replace this line
tree := pixel.NewSprite(spritesheet, pixel.R(0, 0, 32, 32))
with this one
tree := pixel.NewSprite(spritesheet, treesFrames[6])
See for yourself, that it works good.
Now that we've got the spritesheet of trees set up, we can go ahead and plant them using mouse!
Window has got a few methods for dealing with the user input. For example, win.Pressed checks whether a key on the keyboard or a button on the mouse is currently pressed down.
However, that's not what we want right now. We only want to plant a new tree when the user clicks the mouse. For that, we can use win.JustPressed which only checks whether a key or a mouse button has just been pressed down.
Just pressed means pressed somewhere between the previous and last call to win.Update
.
There's also one more important method, win.MousePosition, which returns the position of the mouse inside the window.
With this knowledge, we can progress and type some code. First of all, let's delete all the lines dealing with the sprite we used to test whether our spritesheet works.
tree := pixel.NewSprite(spritesheet, treesFrames[6]) // delete
tree.SetMatrix(pixel.IM.Scaled(0, 16).Moved(win.Bounds().Center())) // delete
for !win.Closed() {
win.Clear(colornames.Whitesmoke)
tree.Draw(win) // delete
win.Update()
}
Now, since we'll be planting many trees, we need to store them somewhere. Let's add an initially empty slice of tree sprites.
var trees []*pixel.Sprite
for !win.Closed() {
win.Clear(colornames.Whitesmoke)
win.Update()
}
Finally, we add the code to actually plant them!
for !win.Closed() {
if win.JustPressed(pixelgl.MouseButtonLeft) {
tree := pixel.NewSprite(spritesheet, treesFrames[rand.Intn(len(treesFrames))])
tree.SetMatrix(pixel.IM.Scaled(0, 4).Moved(win.MousePosition()))
trees = append(trees, tree)
}
Here we create a new sprite with a random tree image from the spritesheet, scale it up four time, set it's position to the current position of the mouse, and add it to the slice of all trees.
One more thing, we need to draw the trees to the window.
win.Clear(colornames.Whitesmoke)
for _, tree := range trees {
tree.Draw(win)
}
win.Update()
Perfect! Run the code and try clicking around to see that everything works!