DEV Community

Cover image for Rust: Create A Clicker Game With Macroquad
Flavius
Flavius

Posted on • Edited on

Rust: Create A Clicker Game With Macroquad

This really quick tutorial is aimed for beginners who want to get started with game/graphics development with Rust and/or Macroquad

Introduction

Hello!
In this really short tutorial, you will learn how to create a simple Clicker with Rust using the Macroquad framework. Macroquad is a simple and easy to use game library for Rust. It supports native desktop apps, WebAssembly, IOS and Android and it is really simple to use.

Perquisites

You must get the follow things down before you proceed with the tutorial:

  • Rust and its basics (even though I'll try to explain each and every detail of the code)

Let's Get Started!

First, create a new Rust project, then add this to the cargo.toml file under the [dependencies] section:



macroquad = "0.3.25" # at the time of writing this tutorial, this was the version
#you can change the version to the latest stable one


Enter fullscreen mode Exit fullscreen mode

Now, navigate to src/main.rs file and let's start writing some code!



use macroquad::prelude::*;

#[macroquad::main("Clicker Game")]
async fn main() {
   loop {
     clear_background(GRAY);

     next_frame().await;
   }
}


Enter fullscreen mode Exit fullscreen mode

Output:
Image description
That's it to get a basic window with a Gray background color, but let's look at it in a little more detail.



use macroquad::prelude::*;


Enter fullscreen mode Exit fullscreen mode

This imports the library that we're going to use (duh).



#[macroquad::main("Clicker Game")]


Enter fullscreen mode Exit fullscreen mode

This tells the program what is the main function, and what the window title should be. Alternatively you can write this in a diferent way for more customizing options:



//snip
fn conf() -> Conf {
 Conf {
   window_title: "Clicker Game", //this field is not optional!
   fullscreen:false,
   //you can add other options too, or just use the default ones:
   ..Default::default()
 }
}
//then pass the function to the attribute
#[macroquad::main(conf)]


Enter fullscreen mode Exit fullscreen mode

Moving on.



async fn main() {
   loop {
     clear_background(GRAY);

     next_frame().await;
   }
}


Enter fullscreen mode Exit fullscreen mode

We make the main function async because Macroquad requires async events. Every Macroquad program needs to have a main game loop, we define it with the loop keyword.
clear_background(GRAY) function tells the program to clear the screen background to the color Gray. There are other colors too, such as RED, GREEN, BLACK, WHITE, SKYBLUE, BLUE, etc. You can pass the color you want to use in the function.
next_frame().await This line tells the program to wait till the next frame is done. Without this, the program will just crash.

Creating the Circle

Let us first define the variables we will need, the x and y position of the circle, the radius, and the score:



   let (x,y) = (screen_width()/2.,screen_height()/2.);
   let r = 70.;
   let circle = Circle::new(x,y,r);
   let mut score = 0;


Enter fullscreen mode Exit fullscreen mode

The first line gets position to the center of the screen. screen_width() gets the total width of the screen, and screen_height() gets the total height.



   let circle = Circle::new(x,y,r);


Enter fullscreen mode Exit fullscreen mode

Here we create a struct called Circle, which has three values, the x and y position, and the radius. We will be needing them in click detection.

Now to draw the circle:



loop {
  clear_background(GRAY);

  draw_circle(x,y,r,RED); 

  next_frame().await;

}


Enter fullscreen mode Exit fullscreen mode

draw_circle() is a function which draws a circle to the screen. It takes in four values, x ,y, radius and the color.

Your code should now look like this:



use macroquad::prelude::*;

#[macroquad::main("CLicker Game")]
async fn main() {
   let (x,y) = (screen_width()/2.,screen_height()/2.);
   let r = 70.;
   let circle = Circle::new(x,y,r);
   let mut score = 0;

   loop {
    clear_background(GRAY);

    draw_circle(x,y,r,RED); 

    next_frame().await;

   }
}


Enter fullscreen mode Exit fullscreen mode

Output:

Output
If it draws a circle on the screen, it'll do!

Detecting Click Events

Let's add some basic click detection working. The way that we're doing in this tutorial is to make a smaller circle struct (of radius 1) and use the .intersect() method on the smaller mouse circle and the main circle struct which we created before. The .intersect() method takes in another struct of the same type, and returns a bool value. If they intersect at any point, it returns true, else it returns false. (Note: there's another way of checking for click on a circular object too with the include() method with takes a Vec2 value. I've used a different approach because it seems easier to understand for beginners.)
Code:



//snip
loop {
   ...
    if is_mouse_button_pressed(MouseButton::Left) {
         let (mouse_x,mouse_y) = mouse_position();
         let mouse_circ = Circle::new(mouse_x,mouse_y,1.);

         if mouse_circ.intersect(&circle) {
            score += 1;
         }
    }

    ...  
   }


Enter fullscreen mode Exit fullscreen mode

is_mouse_button_pressed is a function which checks if a certain mouse button is pressed and returns a bool value. MouseButton is an enum which contains the values for Left mouse button, the Right mouse button and the Middle mouse button, and the Unknown variant if the value is not known.



let (mouse_x,mouse_y) = mouse_position();
let mouse_circ = Circle::new(mouse_x,mouse_y,1.);


Enter fullscreen mode Exit fullscreen mode

The first line gets the mouse position at the time of calling that event and stores in the tuple values. We now create the mouse circle with the mouse position and the radius of 1. (Note that: 1. is equal to 1.00. The radius is an f32 value).



if mouse_circ.intersect(&circle) {
     score += 1;
}


Enter fullscreen mode Exit fullscreen mode

Now we use the intersect function, and if it does intersect, we add the score value with 1. You can change the increment value to whatever you want.

Displaying Title And Score

To draw text, Macroquad provides us with the draw_text() function. It takes in the text to display, the x and y positions, the font size and the color of the text.
Add this inside the loop:



//snip
draw_text("Clicker Game",screen_width()/2.-100.,100.,50.,WHITE);


Enter fullscreen mode Exit fullscreen mode

This draws the text "Clicker Game" to the top of the screen.
Displaying the score:



draw_text(format!("Clicks: {}",score).as_str(),screen_width()/2.-100.,500.,50.,WHITE);



Enter fullscreen mode Exit fullscreen mode

We first concatenate the score variable with some other text and use the as_str() method because draw_text() accepts only an str value and not a String value. We also set the y position to somewhere below the circle, in this case, 500.

And That's It!

You've successfully created a clicker game in Rust! Now just run your project using cargo run.
Here's the full code:



use macroquad::prelude::*;

#[macroquad::main("CLicker Game")]
async fn main() {
   let (x,y) = (screen_width()/2.,screen_height()/2.);
   let r = 70.;
   let circle = Circle::new(x,y,r);
   let mut score = 0;

   loop {
      clear_background(GRAY);

      if is_mouse_button_pressed(MouseButton::Left) {
         let (mouse_x,mouse_y) = mouse_position();
         let mouse_circ = Circle::new(mouse_x,mouse_y,1.);

         if  circle.overlaps(&mouse_circ){
            score+=1;
         }
      }


      draw_text("Clicker Game",screen_width()/2.-100.,100.,50.,WHITE);
      draw_text(format!("Clicks: {}",score).as_str(),screen_width()/2.-100.,500.,50.,WHITE);
      draw_circle(x,y,r,RED);
      next_frame().await;
   }
}



Enter fullscreen mode Exit fullscreen mode

Output:

Output
If there are any corrections, please do inform me.
I will make a part 2 with adding sounds and other stuff. If I don't, remind me!
Good Bye 👋

Other Useful Links:

Macroquad Official Website
Macroquad docs.rs
Macroquad Github

Top comments (2)

Collapse
 
jaim_animation profile image
Jaim_animation

The circle.intersects(&self, &other) method is no longer present. Instead, try using circle.overlaps(&self, &other).

Collapse
 
liftoffstudios profile image
Liftoff Studios

Awesome! I'd never heard Rust library of his before!!! Thanks a ton