20. Project: A Simple Clicker Game¶
Create a game where you click items using the content from Tutorials 3-19.
20.1 Item Drawing and Click Detection¶
- Prepare a target emoji and a circle for click detection
- When clicked, move to a random position
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
while (System::Update())
{
// If click target is clicked
if (targetCircle.leftClicked())
{
// Change click target position to random position
targetCircle.center = GetRandomPos();
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
}
}
20.2 Mouse Over Detection¶
- When the click target is being moused over, change the mouse cursor to a hand shape
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
while (System::Update())
{
// If click target overlaps with mouse cursor
if (targetCircle.mouseOver())
{
// Change mouse cursor to hand shape
Cursor::RequestStyle(CursorStyle::Hand);
}
// If click target is clicked
if (targetCircle.leftClicked())
{
// Change click target position to random position
targetCircle.center = GetRandomPos();
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
}
}
20.3 Score Display¶
- Add score when clicking items
- Display score on screen
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
// Score
int32 score = 0;
while (System::Update())
{
// If click target overlaps with mouse cursor
if (targetCircle.mouseOver())
{
// Change mouse cursor to hand shape
Cursor::RequestStyle(CursorStyle::Hand);
}
// If click target is clicked
if (targetCircle.leftClicked())
{
score += 100;
// Change click target position to random position
targetCircle.center = GetRandomPos();
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
// Display score
font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
}
}
20.4 Adding Obstacle Items¶
- Add obstacle items that reduce points when clicked
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Obstacle item emoji
const Texture trapEmoji{ U"🌶"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
// Obstacle item circle
Circle trapCircle{ 200, 150, 60 };
// Score
int32 score = 0;
while (System::Update())
{
// If click target overlaps with mouse cursor
if (targetCircle.mouseOver() || trapCircle.mouseOver())
{
// Change mouse cursor to hand shape
Cursor::RequestStyle(CursorStyle::Hand);
}
// If click target is clicked
if (targetCircle.leftClicked())
{
score += 100;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
// If obstacle item is clicked
if (trapCircle.leftClicked())
{
score -= 200;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
// Draw obstacle item
trapEmoji.drawAt(trapCircle.center);
// Display score
font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
}
}
20.5 Remaining Time¶
- Set a time limit for the game and prevent item clicking when time runs out
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Obstacle item emoji
const Texture trapEmoji{ U"🌶"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
// Obstacle item circle
Circle trapCircle{ 200, 150, 60 };
// Score
int32 score = 0;
// Remaining time
double remainingTime = 10.0;
while (System::Update())
{
const double deltaTime = Scene::DeltaTime();
// Reduce remaining time
remainingTime -= deltaTime;
if (0 < remainingTime) // If there's remaining time
{
// If click target overlaps with mouse cursor
if (targetCircle.mouseOver() || trapCircle.mouseOver())
{
// Change mouse cursor to hand shape
Cursor::RequestStyle(CursorStyle::Hand);
}
// If click target is clicked
if (targetCircle.leftClicked())
{
score += 100;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
// If obstacle item is clicked
if (trapCircle.leftClicked())
{
score -= 200;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
// Draw obstacle item
trapEmoji.drawAt(trapCircle.center);
// Display score
font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
if (0 < remainingTime) // If there's remaining time
{
// Display remaining time
font(U"TIME: {:.1f}"_fmt(remainingTime)).draw(40, Arg::topRight(760, 40), ColorF{ 0.1 });
}
else // If time is up
{
// Display time's up message
font(U"TIME'S UP!").drawAt(60, Vec2{ 400, 300 }, ColorF{ 0.1 });
}
}
}
20.6 【Complete】Game Reset¶
- Press Enter on the game over screen to reset score and remaining time and replay the game
Code
# include <Siv3D.hpp>
// Function that returns random coordinates
Vec2 GetRandomPos()
{
return{ Random(60, 740), Random(60, 540) };
}
void Main()
{
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
// Click target emoji
const Texture targetEmoji{ U"🍎"_emoji };
// Obstacle item emoji
const Texture trapEmoji{ U"🌶"_emoji };
// Click target circle
Circle targetCircle{ 400, 300, 60 };
// Obstacle item circle
Circle trapCircle{ 200, 150, 60 };
// Score
int32 score = 0;
// Remaining time
double remainingTime = 10.0;
while (System::Update())
{
const double deltaTime = Scene::DeltaTime();
// Reduce remaining time
remainingTime -= deltaTime;
if (0 < remainingTime) // If there's remaining time
{
// If click target overlaps with mouse cursor
if (targetCircle.mouseOver() || trapCircle.mouseOver())
{
// Change mouse cursor to hand shape
Cursor::RequestStyle(CursorStyle::Hand);
}
// If click target is clicked
if (targetCircle.leftClicked())
{
score += 100;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
// If obstacle item is clicked
if (trapCircle.leftClicked())
{
score -= 200;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
}
else // If time is up
{
if (KeyEnter.down())
{
score = 0;
remainingTime = 15.0;
targetCircle.center = GetRandomPos();
trapCircle.center = GetRandomPos();
}
}
// Draw click target
targetEmoji.drawAt(targetCircle.center);
// Draw obstacle item
trapEmoji.drawAt(trapCircle.center);
// Display score
font(U"SCORE: {}"_fmt(score)).draw(40, Vec2{ 40, 40 }, ColorF{ 0.1 });
if (0 < remainingTime) // If there's remaining time
{
// Display remaining time
font(U"TIME: {:.1f}"_fmt(remainingTime)).draw(40, Arg::topRight(760, 40), ColorF{ 0.1 });
}
else // If time is up
{
// Display time's up message
font(U"TIME'S UP!").drawAt(60, Vec2{ 400, 300 }, ColorF{ 0.1 });
font(U"Press [Enter] to restart").drawAt(40, Vec2{ 400, 400 }, ColorF{ 0.1 });
}
}
}