1
+ import pygame
2
+ from pygame .locals import *
3
+ from threading import Thread
4
+ import time
5
+
6
+
7
+ """
8
+ Informations:
9
+ Simple script that shows how to make 2d waves with Pygame.
10
+
11
+ I created this program with a good tutorial on this page:
12
+ --> https://gamedevelopment.tutsplus.com/tutorials/make-a-splash-with-dynamic-2d-water-effects--gamedev-236
13
+ This program use the Threads, with this module you can change the FPS constants destined to Pygame and consequently accelerate the program
14
+ but nothing changes because the Update() function is not connected with the Pygame's framerate.
15
+ There are other methods but i have chosen this one.
16
+
17
+ This program is not very fast because too much springs are used, you can modify the script to optimize it.
18
+ You can release some springs and create a draw system with 2d polygons to make the script faster and more optimized.
19
+ """
20
+
21
+
22
+ # Constants
23
+ SCREEN_WIDTH = 400
24
+ SCREEN_HEIGHT = 400
25
+ CAPTION = "Waves Simulation with Pygame"
26
+ FPS = 60
27
+ BLUE = (0 , 103 , 247 )
28
+ WHITE = (255 , 255 , 255 )
29
+
30
+
31
+ class Main :
32
+ def __init__ (self ):
33
+ """Main class"""
34
+
35
+ self .screen = pygame .display .set_mode ((SCREEN_WIDTH , SCREEN_HEIGHT ))
36
+ pygame .display .set_caption (CAPTION )
37
+
38
+ self .time = pygame .time .Clock ()
39
+
40
+ self .target_height = 200 ## Max wave height and stable point
41
+ self .tension = 0.025
42
+ self .dampening = 0.020
43
+ self .spread = 0.25
44
+ self .height_splash = 5 ## Number of loop round // Warnning with a too hight value on this variable, the script will be very slower
45
+ self .init_speed = 200 ## Initial speed
46
+ self .init_position_x = 0 ## first position
47
+ self .final_position_x = 400 ## second_position
48
+
49
+ self .springs = [] ## List of objects (here the objects are Springs)
50
+
51
+ self .number_rod = self .create_number_rod ()
52
+ self .rod_width = self .create_rod_width ()
53
+ self .init_index = int (self .number_rod / self .rod_width )
54
+ self .create_spring_list (self .rod_width )
55
+ self .start_water_waves (self .rod_width )
56
+
57
+ self .done = False
58
+
59
+ self .run_game ()
60
+
61
+
62
+ def run_game (self ):
63
+ """Main loop"""
64
+
65
+ while not self .done :
66
+ for event in pygame .event .get ():
67
+ if event .type == QUIT :
68
+ self .done = True
69
+ pygame .quit ()
70
+ exit ()
71
+ if event .type == MOUSEBUTTONUP :
72
+ if self .counter .done == True :
73
+ pos = pygame .mouse .get_pos ()
74
+ self .init_index = int (pos [0 ] / self .final_position_x * self .number_rod )
75
+ self .start_water_waves (self .rod_width )
76
+
77
+ self .draw_level ()
78
+
79
+ for spring in self .springs :
80
+ spring .draw (self .screen )
81
+
82
+ self .time .tick (FPS ) ## Set a FPS constants but you can change it
83
+ pygame .display .flip ()
84
+
85
+
86
+ def create_number_rod (self ):
87
+ """Have half rod on all the water"""
88
+
89
+ return abs (int ((self .init_position_x - self .final_position_x ) / 2 ))
90
+
91
+
92
+ def create_rod_width (self ):
93
+ """Make an size average thank to the number of rod"""
94
+
95
+ return int ((self .init_position_x + self .final_position_x ) / self .number_rod )
96
+
97
+
98
+ def create_spring_list (self , rod_width ):
99
+ """Create the spring list"""
100
+
101
+ for x in range (self .init_position_x , self .final_position_x , rod_width ):
102
+ self .springs .append (Spring (x , self .target_height , rod_width ))
103
+
104
+
105
+ def start_water_waves (self , rod_width ):
106
+ """Start the waves motion at a given position"""
107
+
108
+ self .springs [self .init_index ].speed = self .init_speed
109
+ self .counter = Counter_Waves_Motion (self )
110
+ self .counter .start ()
111
+
112
+
113
+ def update (self ):
114
+ """Update the waves"""
115
+
116
+ self .update_waves ()
117
+ self .create_neighbour_list ()
118
+ self .update_neighbour ()
119
+ self .test_end_water_waves ()
120
+
121
+
122
+ def test_end_water_waves (self ):
123
+ """Check if the waves are still alive"""
124
+
125
+ count = 0
126
+
127
+ for i in range (len (self .springs )):
128
+ if not int (self .springs [i ].speed ) and not int (self .springs [i ].y ):
129
+ count += 1
130
+
131
+ if count == len (self .springs ):
132
+ self .stop_water_motion ()
133
+
134
+
135
+ def stop_water_motion (self ):
136
+ """Stop the motion of the water"""
137
+
138
+ for i in range (len (self .springs )):
139
+ self .springs [i ].speed = 0
140
+ self .springs [i ].height = self .target_height
141
+ self .springs [i ].y = 0
142
+
143
+
144
+ def update_waves (self ):
145
+ """Update the water waves
146
+ of all the rod in the springs list"""
147
+
148
+ for i in range (len (self .springs )):
149
+ self .springs [i ].update (self .dampening , self .tension , self .target_height )
150
+
151
+
152
+ def create_neighbour_list (self ):
153
+ """Create the lists of height
154
+ difference between the rods without reference"""
155
+
156
+ self .lDeltas = list (self .springs )
157
+ self .rDeltas = list (self .springs )
158
+
159
+
160
+ def update_neighbour (self ):
161
+ """Update the rod beside another"""
162
+
163
+ for j in range (self .height_splash ):
164
+ for i in range (len (self .springs )):
165
+
166
+ if i > 0 :
167
+ self .lDeltas [i ] = self .spread * (self .springs [i ].height - self .springs [i - 1 ].height )
168
+ self .springs [i - 1 ].speed += self .lDeltas [i ]
169
+
170
+ if i < len (self .springs ) - 1 :
171
+ self .rDeltas [i ] = self .spread * (self .springs [i ].height - self .springs [i + 1 ].height )
172
+ self .springs [i + 1 ].speed += self .rDeltas [i ]
173
+
174
+ self .termine_update_neighbour ()
175
+
176
+
177
+ def termine_update_neighbour (self ):
178
+ """Terminate the procedure"""
179
+
180
+ for i in range (len (self .springs )):
181
+ if i > 0 :
182
+ self .springs [i - 1 ].height += self .lDeltas [i ]
183
+ if i < len (self .springs ) - 1 :
184
+ self .springs [i + 1 ].height += self .rDeltas [i ]
185
+
186
+
187
+ def draw_level (self ):
188
+ """Draw the level background"""
189
+
190
+ self .screen .fill (WHITE )
191
+
192
+
193
+
194
+ class Spring :
195
+ def __init__ (self , x , target_height , rod_width ):
196
+ """Spring Creator, one spring by one"""
197
+
198
+ self .x = x
199
+ self .y = 0
200
+ self .speed = 0
201
+ self .height = target_height
202
+ self .bottom = 400
203
+ self .rod_width = rod_width
204
+
205
+
206
+ def update (self , dampening , tension , target_height ):
207
+ """Update the springs, this is numerical integration
208
+ and called Euler Method, it is simple fast and usual"""
209
+
210
+ self .y = target_height - self .height
211
+ self .speed += tension * self .y - self .speed * dampening
212
+ self .height += self .speed
213
+
214
+
215
+ def draw (self , screen ):
216
+ """Draw the spring,
217
+ (Here this is a simple rod)"""
218
+
219
+ pygame .draw .line (screen , BLUE , (self .x , self .height ), (self .x , self .bottom ), self .rod_width )
220
+
221
+
222
+ class Counter_Waves_Motion (Thread ):
223
+ def __init__ (self , main ):
224
+ Thread .__init__ (self )
225
+ """Thread init class, (counter class)"""
226
+
227
+ self .main = main
228
+
229
+ self .time = 0
230
+ self .max_time = 2
231
+ self .time_sleep = 0.01
232
+
233
+ self .done = False
234
+
235
+
236
+ def run (self ):
237
+ """Run the Thread, (parallel programming)"""
238
+
239
+ while not self .done :
240
+ self .main .update ()
241
+
242
+ self .time += self .time_sleep
243
+
244
+ if self .time >= self .max_time :
245
+ self .done = True
246
+
247
+ time .sleep (self .time_sleep )
248
+
249
+
250
+
251
+ if __name__ == "__main__" :
252
+ obj_main = Main ()
253
+
254
+ ## In this program all is OOP
0 commit comments