The provided web content describes a tutorial on creating a "suica game" (a watermelon-themed game) using the Pygame library in Python.
Abstract
The "suica game" is a puzzle game where players combine fruits by dropping them into a box to eventually create a watermelon. The tutorial explains the game's rules, including fruit upgrades upon contact, the space each fruit occupies, and the game-ending condition when fruits reach the top of the box. It guides the reader through setting up the game environment with Pygame, handling mouse events to drop fruits, and implementing the game's core logic where same-colored fruits combine to form larger fruits, ultimately leading to a watermelon. The tutorial also covers additional features such as boundary constraints, object overlapping, and a score system. It concludes by providing the full source code and inviting readers to explore the game further on GitHub.
Opinions
The tutorial presents the game as a fun and engaging project for readers interested in game development with Pygame.
It emphasizes the importance of understanding basic game mechanics, such as collision detection and object state management.
The author suggests that even a simple game like the "suica game" can incorporate complex features like a scoring system and varying levels of difficulty.
The tutorial implies that Python and Pygame are accessible tools for beginners in game development, making the creation of such games within reach for novice programmers.
By providing a complete example and linking to a GitHub repository, the author encourages experimentation and further learning beyond the tutorial.
Make your own suica game (watermelon game) using Pygame
What is suica game?
suica means watermelon in japanese, as the name suggested this game’s goal is to create a watermelon. by dropping other fruits to the box, same type of fruit will turned into a larger fruit and finally watermelon is made. check out a youtuber who speed run watermelon👇
Rules
Same type of fruit upgrade when contact
Each fruit take up specific space
When fruits reach the top game is over
Create background
draw 3 lines to create a rectangle with top side open
In pygame we can get set condition triggered by user mouse click and get the positions of the mouse. With that we can append circles properties to a list when user clicked mouse
if event.type == pygame.MOUSEBUTTONDOWN:
# draw a white ball when mouse is clicked
ball_y = event.pos[1]
ball_x = event.pos[0]
balls.append([ball_x, ball_y])
With the positions of all circles, we can draw them using for loop, notice that use 720 — radius to check the position of cricle, this is because we only have center position of circle and we don’t want part of the circle go outside the container.
for ball in balls:
# bacasue downward in pygame is positiveif ball[1] < 720 - radius:
ball[1] += ball_speed
else:
ball[1] = 720 - radius
white_ball = pygame.draw.circle(screen, white, (ball[0], ball[1]), radius)
two circles combine into bigger one
The most important feature of watermelon game is grade fruits with two same type fruits
we can do this in python by creating a color order and with a condition checking
the logic here are:
check if two circles is in touch ( distance < r1 + r2), you can use Euclidean distance math.dist()
if they are in touch check if they are same color
if they have same color, append both circles to a ready_to_remove list and append a new circle with higher level to a ready_to_add list
update the original circles list based on ready_to_remove and ready_to_add lists
Final result
Although this is a mini game, there are a tons of game feature to implement, such as boundary constrain, object overlapping, score system. You can get the code below or go to GitHub repo
import pygame
import math
import random
screen_width = 600
screen_height = 800
skwed_probabitity = [0.7, 0.1, 0.05, 0.08, 0.04, 0.02, 0.01, 0, 0]
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
orange = (255,165,0)
yellow = (255,255,0)
green = (0 , 255 , 0)
blue = (0,0,255)
thistle = (255, 0, 255)
hot_pink = (255, 105, 180)
purple = (128, 0, 128)
color_order = [white, red, orange,yellow, green, blue, thistle, hot_pink, purple]
screen = pygame.display.set_mode((screen_width, screen_height))
# define scores of each ball
white_score = 0
red_score = 2
orange_score = 4
yellow_score = 8
green_score = 8
blue_score = 16
thistle_score = 32
hot_pink_score = 64
purple_score = 128
score = 0
score_order = [white_score, red_score, orange_score, yellow_score, green_score, blue_score, thistle_score, hot_pink_score, purple_score]
# define ball speed and redius
ball_speed = 1
white_radius = 15
red_radius = 22
orange_radius = 42
yellow_radius = 55
green_radius = 68
blue_radius = 85
thistle_radius = 94
hot_pink_radius = 110
purple_radius = 130
radius_order = [white_radius, red_radius, orange_radius, yellow_radius, green_radius, blue_radius, thistle_radius, hot_pink_radius, purple_radius]
gravity = 0.05
balls = []
balls_to_add = []
balls_to_remove = []
pygame.init()
running = True
gaem_over = False
index = range(len(color_order))
random_int = random.choices(index, weights=skwed_probabitity, k=1)[0]
next_ball_radius = radius_order[random_int]
next_ball_color = color_order[random_int]
while running:
screen.fill(black)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = Falseelif event.type == pygame.MOUSEBUTTONDOWN:
if event.pos[0] > 40and event.pos[0]<560and event.pos[1]< 40:
# draw a ball when mouse is clicked
ball_x = event.pos[0]
ball_y = event.pos[1]
horizontal_speed = 0
is_dropping = True
balls.append([ball_x, ball_y, ball_speed, horizontal_speed, next_ball_color, next_ball_radius, is_dropping])
next_index = random.choices(index, weights=skwed_probabitity, k=1)[0]
next_ball_color = color_order[next_index]
next_ball_radius = radius_order[next_index]
elif event.type == pygame.MOUSEMOTION and event.pos[0] > 40and event.pos[0]<560and event.pos[1]< 40:
# draw next ball on mouse motion
mouse_x = event.pos[0]
mouse_y = event.pos[1]
pygame.draw.circle(screen, next_ball_color, (mouse_x, mouse_y), next_ball_radius)
ifnot gaem_over:
for i inrange(len(balls)):
for j inrange(i+1, len(balls)):
dx = balls[i][0] - balls[j][0]
dy = balls[i][1] - balls[j][1]
distance = math.sqrt(dx **2 + dy **2)
if distance < balls[i][5] + balls[j][5]:
# check if the balls are of same colorif balls[i][4] == balls[j][4]:
color_index = color_order.index(balls[i][4])
# handle special case when two last order balls collide they will be removed and score will be doubledif color_index == len(color_order) - 1:
score += score_order[color_index] * 2if color_index + 1 < len(color_order):
# add a new ball with next color# sum horizontal speed after collision
new_x = (balls[i][0] + balls[j][0]) / 2 + random.uniform(-1,1)
new_y = (balls[i][1] + balls[j][1]) / 2 + random.uniform(0,1)
balls_to_add.append([new_x, new_y, balls[i][2] , balls[i][3] + balls[j][3], color_order[color_index + 1], radius_order[color_index + 1], False])
# update score
score += score_order[color_index + 1]
# remove the balls of same color
balls_to_remove.append(balls[i])
balls_to_remove.append(balls[j])
break# if they touch but not of same colorelse:
# check overlap
overlap = balls[i][5] + balls[j][5] - distance
dx = dx / distance
dy = dy / distance
balls[i][0] += dx * overlap / 2
balls[i][1] += dy * overlap / 2
balls[j][0] -= dx * overlap / 2
balls[j][1] -= dy * overlap / 2# update speed of both balls when they collide
balls[i][2] = 0
balls[j][2] = 0
balls[i][3] *= 1
balls[j][3] *= 1# update the is_dropping flag
balls[i][6] = False
balls[j][6] = False# remove and add ballsfor ball in balls_to_remove:
if ball in balls:
balls.remove(ball)
for ball in balls_to_add:
balls.append(ball)
# empty the lists
balls_to_remove.clear()
balls_to_add.clear()
# draw ballsfor ball in balls:
box_top = 40# if ball is not dropping and above the box, game overif (not ball[6]) and ball[1] - ball[5] < box_top:
gaem_over = Truebreak
ball[2] += gravity
box_bottom = 720 - ball[5]
box_left = 40 + ball[5]
box_right = 560 - ball[5]
if ball[1] >= box_bottom:
ball[1] = box_bottom
elif ball[1] < box_bottom:
ball[1] += ball[2]
# update x position of ball
ball[0] += ball[3]
# check if ball is out of box horizontallyif ball[0] < box_left:
ball[0] = box_left + 1
ball[3] *= -0.5elif ball[0] > box_right:
ball[0] = box_right - 1
ball[3] *= -0.5
any_ball = pygame.draw.circle(screen, ball[4], (ball[0], ball[1]), ball[5])
# draw 3 lines to make a rectangle with upper side open
pygame.draw.line(screen, white, (40, 40), (40, 720), 1)
pygame.draw.line(screen, white, (40, 720), (560, 720), 1)
pygame.draw.line(screen, white, (560, 720), (560, 40), 1)
# draw a warning line at top of box with red dotted line
dotted_line_y = 40
dotted_line_length = 4
dotted_line_space = 4# grey color dotted line
dotted_line_color = (128,128,128)
for x inrange(40, 560, dotted_line_length + dotted_line_space):
pygame.draw.line(screen, dotted_line_color, (x, dotted_line_y), (x + dotted_line_length, dotted_line_y), 1)
# draw color order
color_order_y = screen_height - 20
color_order_spacing = 40for i, color inenumerate(color_order):
pygame.draw.circle(screen, color, (40 + i * color_order_spacing, color_order_y), 10)
# draw score
font = pygame.font.Font(None, 36)
score_text = font.render(f'Score: {score}', True, white)
screen.blit(score_text, (screen_width /2 - score_text.get_width()/2, 20))
pygame.display.update()
if gaem_over:
font = pygame.font.Font(None, 36)
game_over_text_lines = [
"GAME OVER",
f"Your Score is: {score}",
"restart by pressing SPACE",
"quit game by pressing ESCAPE"
]
for i, line inenumerate(game_over_text_lines):
line_text = font.render(line, True, white)
screen.blit(line_text, (screen_width / 2 - line_text.get_width() / 2, screen_height / 2 - line_text.get_height() / 2 + i * line_text.get_height()))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = Falseelif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
# reset gaem when space is pressed
balls.clear()
balls_to_add.clear()
balls_to_remove.clear()
score = 0
gaem_over = Falseelif event.key == pygame.K_ESCAPE:
# quit game when esc is pressed
running = False
pygame.quit()