-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathBreakout!_game.rb
237 lines (191 loc) · 4.92 KB
/
Breakout!_game.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# Please note that you need to have the Ruby2D gem installed to run this code successfully.
require 'ruby2d'
WIDTH, HEIGHT = 800, 600
set width: WIDTH, height: HEIGHT
set title: "Sk. Salahuddin - Morning 01 Batch"
FPS = 60
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
BALL_RADIUS = 10
LIVES_FONT = 'resources/Roboto-Regular.ttf', 40
class Paddle
VEL = 5
attr_accessor :x, :y, :width
def initialize(x, y, width, height, color)
@x = x
@y = y
@width = width
@height = height
@color = color
end
def draw
Rectangle.new(x: @x, y: @y, width: @width, height: @height, color: @color)
end
def move(direction = 1)
@x += VEL * direction
end
end
class Ball
VEL = 5
attr_accessor :x, :y, :x_vel, :y_vel
def initialize(x, y, radius, color)
@x = x
@y = y
@radius = radius
@color = color
@x_vel = 0
@y_vel = -VEL
end
def move
@x += @x_vel
@y += @y_vel
end
def set_vel(x_vel, y_vel)
@x_vel = x_vel
@y_vel = y_vel
end
def draw
Circle.new(x: @x, y: @y, radius: @radius, sectors: 32, color: @color)
end
end
class Brick
attr_accessor :health
def initialize(x, y, width, height, health, colors)
@x = x
@y = y
@width = width
@height = height
@health = health
@max_health = health
@colors = colors
@color = colors[0]
end
def draw
Rectangle.new(x: @x, y: @y, width: @width, height: @height, color: @color)
end
def collide(ball)
return false unless (ball.x <= @x + @width) && (ball.x >= @x)
return false unless ball.y - ball.radius <= @y + @height
hit
ball.set_vel(ball.x_vel, ball.y_vel * -1)
true
end
def hit
@health -= 1
@color = interpolate(*@colors, @health.to_f / @max_health)
end
def interpolate(color_a, color_b, t)
r = (color_a.red + (color_b.red - color_a.red) * t).to_i
g = (color_a.green + (color_b.green - color_a.green) * t).to_i
b = (color_a.blue + (color_b.blue - color_a.blue) * t).to_i
Color.new(r, g, b)
end
end
def draw(paddle, ball, bricks, lives)
clear
paddle.draw
ball.draw
bricks.each(&:draw)
Text.new("Lives: #{lives}", x: 10, y: HEIGHT - 10 - LIVES_FONT[1], font: LIVES_FONT[0], size: LIVES_FONT[1]).draw
end
def ball_collision(ball)
if ball.x - BALL_RADIUS <= 0 || ball.x + BALL_RADIUS >= WIDTH
ball.set_vel(ball.x_vel * -1, ball.y_vel)
end
if ball.y + BALL_RADIUS >= HEIGHT || ball.y - BALL_RADIUS <= 0
ball.set_vel(ball.x_vel, ball.y_vel * -1)
end
end
def ball_paddle_collision(ball, paddle)
return unless ball.x <= paddle.x + paddle.width && ball.x >= paddle.x
return unless ball.y + ball.radius >= paddle.y
paddle_center = paddle.x + paddle.width / 2
distance_to_center = ball.x - paddle_center
percent_width = distance_to_center / paddle.width
angle = percent_width * 90
angle_radians = Math::PI * angle / 180
x_vel = Math.sin(angle_radians) * ball.VEL
y_vel = Math.cos(angle_radians) * ball.VEL * -1
ball.set_vel(x_vel, y_vel)
end
def generate_bricks(rows, cols)
gap = 2
brick_width = WIDTH / cols - gap
brick_height = 20
bricks = []
rows.times do |row|
cols.times do |col|
brick = Brick.new(
col * brick_width + gap * col,
row * brick_height + gap * row,
brick_width,
brick_height,
2,
[Color.new(0, 255, 0), Color.new(255, 0, 0)]
)
bricks << brick
end
end
bricks
end
def reset(paddle, ball)
paddle.x = WIDTH / 2 - PADDLE_WIDTH / 2
paddle.y = HEIGHT - PADDLE_HEIGHT - 5
ball.x = WIDTH / 2
ball.y = paddle.y - BALL_RADIUS
end
def display_text(text)
Text.new(
text,
x: WIDTH / 2 - text.size / 2,
y: HEIGHT / 2 - LIVES_FONT[1] / 2,
font: LIVES_FONT[0],
size: LIVES_FONT[1],
color: 'red'
).draw
show
sleep(3)
end
paddle_x = WIDTH / 2 - PADDLE_WIDTH / 2
paddle_y = HEIGHT - PADDLE_HEIGHT - 5
paddle = Paddle.new(paddle_x, paddle_y, PADDLE_WIDTH, PADDLE_HEIGHT, 'white')
ball = Ball.new(WIDTH / 2, paddle_y - BALL_RADIUS, BALL_RADIUS, 'red')
bricks = generate_bricks(3, 10)
lives = 3
reset(paddle, ball)
on :key_held do |event|
case event.key
when 'left'
paddle.move(-1) if paddle.x - paddle.VEL >= 0
when 'right'
paddle.move(1) if paddle.x + paddle.width + paddle.VEL <= WIDTH
end
end
update do
ball.move
ball_collision(ball)
ball_paddle_collision(ball, paddle)
bricks.each do |brick|
bricks.delete(brick) if brick.collide(ball) && brick.health <= 0
end
if ball.y + ball.radius >= HEIGHT
lives -= 1
ball.x = paddle.x + paddle.width / 2
ball.y = paddle.y - BALL_RADIUS
ball.set_vel(0, ball.VEL * -1)
end
if lives <= 0
bricks = generate_bricks(3, 10)
lives = 3
reset(paddle, ball)
display_text("You Lost!")
end
if bricks.empty?
bricks = generate_bricks(3, 10)
lives = 3
reset(paddle, ball)
display_text("You Won!")
end
draw(paddle, ball, bricks, lives)
end
show