Skip to content

Commit c9b60c8

Browse files
authored
Merge branch 'ppap-1264589:main' into main
2 parents 5898a76 + 7e40567 commit c9b60c8

21 files changed

+453
-71
lines changed

.classpath

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
4+
<attributes>
5+
<attribute name="module" value="true"/>
6+
</attributes>
7+
</classpathentry>
8+
<classpathentry kind="src" path="src"/>
9+
<classpathentry excluding="src/|res/" kind="src" path=""/>
10+
<classpathentry kind="src" path="res"/>
11+
<classpathentry kind="output" path="bin"/>
12+
</classpath>

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin/

.project

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>SparrowAdventure</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
</buildSpec>
14+
<natures>
15+
<nature>org.eclipse.jdt.core.javanature</nature>
16+
</natures>
17+
</projectDescription>

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1+
# Bài tập lớn Lập trình Hướng đối tượng (OOP)
2+
3+
14
<p align="center">
25
<a href="https://git.io/typing-svg"><img src="https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&center=true&vCenter=true&random=false&width=450&lines=Sparrow+Adventure" alt="Typing SVG" /></a>
36
</p>
47
<div align="center">
8+
<img alt="Static Badge" src="https://img.shields.io/badge/HUST-course-blue?labelColor=EE4E4E&color=151515">
59
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/ppap-1264589/SparrowAdventure?labelColor=7AA2E3&color=97E7E1">
610
</div>
711

8-
## Đây là phần code bài tập lớn OOP lớp thầy Trần Nhật Hóa
12+
## Giới thiệu
13+
"Sparrow Adventure" là một game RPG được phát triển trong bài tập lớn OOP của nhóm thuộc lớp của thầy Trần Nhật Hóa, ngôn ngữ chính sử dụng Java. Trong game, người chơi điều khiển Captain Jack Sparrow để khám phá các vùng đất, chiến đấu với kẻ thù và tìm kiếm kho báu Aztec. Game cung cấp các tính năng như di chuyển, tấn công, thu thập vật phẩm, quản lý kho đồ, và hệ thống tính điểm dựa trên số lượng kẻ thù bị hạ và kho báu thu thập được.
14+
## Thành viên
15+
- Đỗ Xuân Hoàng
16+
- Đoàn Xuân Công Đạt
17+
- Nguyễn Tùng Dương
18+
- Nguyễn Trung Hiếu
19+
- Nguyễn Hoài Phương
20+
21+
## Người đóng góp
22+
23+
<div align="center">
24+
<a href="https://github.com/ppap-1264589/SparrowAdventure/graphs/contributors">
25+
<img src="https://contrib.rocks/image?repo=ppap-1264589/SparrowAdventure" />
26+
</a>
27+
</div>
28+
29+
## Tính năng
30+
31+
## Cài đặt và triển khai
932

1033

1134
Để mang một bộ code về chạy thử trên Eclipse, thực hiện các bước sau:
@@ -49,3 +72,6 @@ https://www.youtube.com/watch?v=6_N8QZ47toY&list=PL4rzdwizLaxYmltJQRjq18a9gsSyEQ
4972
Bộ code dự kiến cần phải hiểu đề bảo vệ bài tập lớn cuối kì:
5073

5174
https://github.com/KaarinGaming/PlatformerTutorial/tree/ep29
75+
76+
## Giải thích
77+
## Minh họa

res/button_atlas.png

22.8 KB
Loading

res/menu_background.png

16.6 KB
Loading

src/audio/AudioPlayer.java

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,162 @@
1+
package audio;
12

3+
import java.io.IOException;
4+
import java.net.URL;
5+
import java.util.Random;
6+
import javax.sound.sampled.*;
7+
8+
public class AudioPlayer {
9+
10+
// Các chỉ số cho các bài nhạc
11+
public static int MENU_1 = 0;
12+
public static int LEVEL_1 = 1;
13+
public static int LEVEL_2 = 2;
14+
15+
// Các chỉ số cho các hiệu ứng âm thanh
16+
public static int DIE = 0; //Chết
17+
public static int JUMP = 1;// Nhảy
18+
public static int GAMEOVER = 2;// Kết thúc trò chơi
19+
public static int LVL_COMPLETED = 3;// hoàn thành level
20+
public static int ATTACK_ONE = 4;// Kĩ năng đánh 1
21+
public static int ATTACK_TWO = 5;// Kĩ năng đánh 2
22+
public static int ATTACK_THREE = 6;// Kĩ năng đánh 3
23+
24+
private Clip[] songs, effects; // Mảng chứa các bài nhạc và hiệu ứng âm thanh
25+
private int currentSongId; // ID bài nhạc đang phát
26+
private float volume = 0.5f; // Mức âm lượng mặc định
27+
private boolean songMute, effectMute; // Trạng thái tắt tiếng của nhạc và hiệu ứng
28+
private Random rand = new Random(); // Để phát hiệu ứng âm thanh ngẫu nhiên
29+
30+
// Constructor: Khởi tạo AudioPlayer và bắt đầu phát nhạc menu
31+
public AudioPlayer() {
32+
loadSongs(); // Nạp các bài nhạc
33+
loadEffects(); // Nạp các hiệu ứng âm thanh
34+
playSong(MENU_1); // Phát nhạc menu
35+
}
36+
37+
// Nạp các file âm thanh cho nhạc nền từ thư mục /audio/
38+
private void loadSongs() {
39+
String[] names = { "menu", "level1", "level2" }; // Tên các file nhạc
40+
songs = new Clip[names.length]; // Tạo mảng chứa các bài nhạc
41+
for (int i = 0; i < songs.length; i++)
42+
songs[i] = getClip(names[i]); // Lấy từng bài nhạc từ file
43+
}
44+
45+
// Nạp các file âm thanh cho hiệu ứng từ thư mục /audio/
46+
private void loadEffects() {
47+
String[] effectNames = { "die", "jump", "gameover", "lvlcompleted", "attack1", "attack2", "attack3" }; // Tên các file hiệu ứng
48+
effects = new Clip[effectNames.length]; // Tạo mảng chứa hiệu ứng âm thanh
49+
for (int i = 0; i < effects.length; i++)
50+
effects[i] = getClip(effectNames[i]); // Lấy từng hiệu ứng từ file
51+
52+
updateEffectsVolume(); // Cập nhật âm lượng cho hiệu ứng âm thanh
53+
}
54+
55+
// Lấy file âm thanh từ đường dẫn và tạo đối tượng Clip
56+
private Clip getClip(String name) {
57+
URL url = getClass().getResource("/audio/" + name + ".wav"); // Đường dẫn tới file âm thanh
58+
AudioInputStream audio;
59+
60+
try {
61+
audio = AudioSystem.getAudioInputStream(url); // Lấy dữ liệu âm thanh từ file
62+
Clip c = AudioSystem.getClip(); // Tạo đối tượng Clip
63+
c.open(audio); // Mở file âm thanh
64+
return c; // Trả về đối tượng Clip
65+
66+
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
67+
e.printStackTrace(); // Bắt và in lỗi nếu có
68+
}
69+
70+
return null; // Trả về null nếu xảy ra lỗi
71+
}
72+
73+
// Đặt âm lượng mới cho cả nhạc nền và hiệu ứng âm thanh
74+
public void setVolume(float volume) {
75+
this.volume = volume;
76+
updateSongVolume(); // Cập nhật âm lượng của nhạc
77+
updateEffectsVolume(); // Cập nhật âm lượng của hiệu ứng
78+
}
79+
80+
// Dừng phát nhạc hiện tại
81+
public void stopSong() {
82+
if (songs[currentSongId].isActive())
83+
songs[currentSongId].stop(); // Dừng bài nhạc nếu nó đang phát
84+
}
85+
86+
// Đặt nhạc nền theo cấp độ (level) trò chơi
87+
public void setLevelSong(int lvlIndex) {
88+
if (lvlIndex % 2 == 0)
89+
playSong(LEVEL_1); // Phát nhạc của level 1 nếu chỉ số là chẵn
90+
else
91+
playSong(LEVEL_2); // Phát nhạc của level 2 nếu chỉ số là lẻ
92+
}
93+
94+
// Khi hoàn thành màn chơi, dừng nhạc và phát hiệu ứng hoàn thành
95+
public void lvlCompleted() {
96+
stopSong();
97+
playEffect(LVL_COMPLETED); // Phát hiệu ứng âm thanh hoàn thành level
98+
}
99+
100+
// Phát hiệu ứng âm thanh tấn công ngẫu nhiên
101+
public void playAttackSound() {
102+
int start = 4; // Các hiệu ứng tấn công bắt đầu từ chỉ số 4
103+
start += rand.nextInt(3); // Chọn ngẫu nhiên giữa các hiệu ứng tấn công
104+
playEffect(start); // Phát hiệu ứng tấn công
105+
}
106+
107+
// Phát hiệu ứng âm thanh
108+
public void playEffect(int effect) {
109+
if (effects[effect].getMicrosecondPosition() > 0)
110+
effects[effect].setMicrosecondPosition(0); // Reset thời gian nếu hiệu ứng đã phát
111+
effects[effect].start(); // Bắt đầu phát hiệu ứng
112+
}
113+
114+
// Phát nhạc nền
115+
public void playSong(int song) {
116+
stopSong();
117+
// Dừng bài nhạc hiện tại
118+
119+
currentSongId = song; // Cập nhật ID bài nhạc mới
120+
updateSongVolume(); // Cập nhật âm lượng
121+
songs[currentSongId].setMicrosecondPosition(0); // Đặt lại vị trí phát nhạc
122+
songs[currentSongId].loop(Clip.LOOP_CONTINUOUSLY); // Phát nhạc liên tục
123+
}
124+
125+
// Chuyển đổi trạng thái tắt/bật tiếng của nhạc nền
126+
public void toggleSongMute() {
127+
this.songMute = !songMute;
128+
for (Clip c : songs) {
129+
BooleanControl booleanControl = (BooleanControl) c.getControl(BooleanControl.Type.MUTE);
130+
booleanControl.setValue(songMute); // Tắt hoặc bật tiếng cho nhạc
131+
}
132+
}
133+
134+
// Chuyển đổi trạng thái tắt/bật tiếng của hiệu ứng âm thanh
135+
public void toggleEffectMute() {
136+
this.effectMute = !effectMute;
137+
for (Clip c : effects) {
138+
BooleanControl booleanControl = (BooleanControl) c.getControl(BooleanControl.Type.MUTE);
139+
booleanControl.setValue(effectMute); // Tắt hoặc bật tiếng cho hiệu ứng
140+
}
141+
if (!effectMute)
142+
playEffect(JUMP); // Nếu bật lại tiếng, phát hiệu ứng nhảy
143+
}
144+
145+
// Cập nhật âm lượng cho nhạc nền
146+
private void updateSongVolume() {
147+
FloatControl gainControl = (FloatControl) songs[currentSongId].getControl(FloatControl.Type.MASTER_GAIN);
148+
float range = gainControl.getMaximum() - gainControl.getMinimum(); // Tính khoảng điều chỉnh âm lượng
149+
float gain = (range * volume) + gainControl.getMinimum(); // Tính giá trị âm lượng mới
150+
gainControl.setValue(gain); // Đặt âm lượng mới cho nhạc nền
151+
}
152+
153+
// Cập nhật âm lượng cho hiệu ứng âm thanh
154+
private void updateEffectsVolume() {
155+
for (Clip c : effects) {
156+
FloatControl gainControl = (FloatControl) c.getControl(FloatControl.Type.MASTER_GAIN);
157+
float range = gainControl.getMaximum() - gainControl.getMinimum(); // Tính khoảng điều chỉnh âm lượng
158+
float gain = (range * volume) + gainControl.getMinimum(); // Tính giá trị âm lượng mới
159+
gainControl.setValue(gain); // Đặt âm lượng mới cho hiệu ứng âm thanh
160+
}
161+
}
162+
}

src/effects/DialogueEffect.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,67 @@
11
package effects;
22

3-
import static utilz.Constants.ANI_SPEED;
4-
import static utilz.Constants.Dialogue.*;
3+
import static utilz.Constants.ANI_SPEED; // Tốc độ hoạt ảnh (animation speed)
4+
import static utilz.Constants.Dialogue.*; // Các hằng số liên quan đến loại đối thoại
55

66
public class DialogueEffect {
77

8-
private int x, y, type;
9-
private int aniIndex, aniTick;
10-
private boolean active = true;
8+
private int x, y, type; // Vị trí (x, y) và loại đối thoại
9+
private int aniIndex, aniTick; // Chỉ số hoạt ảnh và số lần tick (đếm thời gian cho hoạt ảnh)
10+
private boolean active = true; // Trạng thái kích hoạt, hiệu ứng có đang hoạt động không
1111

12+
// Constructor: Khởi tạo hiệu ứng đối thoại với vị trí (x, y) và loại đối thoại
1213
public DialogueEffect(int x, int y, int type) {
1314
this.x = x;
1415
this.y = y;
1516
this.type = type;
1617
}
1718

19+
// Cập nhật hiệu ứng đối thoại theo thời gian (tick), xử lý hoạt ảnh
1820
public void update() {
19-
aniTick++;
20-
if (aniTick >= ANI_SPEED) {
21-
aniTick = 0;
22-
aniIndex++;
23-
if (aniIndex >= GetSpriteAmount(type)) {
24-
active = false;
25-
aniIndex = 0;
21+
aniTick++; // Tăng số lần tick
22+
if (aniTick >= ANI_SPEED) { // Khi số tick đạt đến tốc độ hoạt ảnh quy định
23+
aniTick = 0; // Reset số tick
24+
aniIndex++; // Tăng chỉ số hoạt ảnh để chuyển sang khung hình tiếp theo
25+
if (aniIndex >= GetSpriteAmount(type)) { // Nếu hoạt ảnh đã hết các khung hình
26+
active = false; // Hiệu ứng kết thúc, đặt trạng thái không hoạt động
27+
aniIndex = 0; // Reset chỉ số hoạt ảnh về 0
2628
}
2729
}
2830
}
2931

32+
// Vô hiệu hóa (deactivate) hiệu ứng đối thoại
3033
public void deactive() {
3134
active = false;
3235
}
3336

37+
// Reset lại hiệu ứng với vị trí mới và kích hoạt lại
3438
public void reset(int x, int y) {
3539
this.x = x;
3640
this.y = y;
37-
active = true;
41+
active = true; // Đặt hiệu ứng hoạt động trở lại
3842
}
3943

44+
// Lấy chỉ số hoạt ảnh hiện tại
4045
public int getAniIndex() {
4146
return aniIndex;
4247
}
4348

49+
// Lấy vị trí x của hiệu ứng
4450
public int getX() {
4551
return x;
4652
}
4753

54+
// Lấy vị trí y của hiệu ứng
4855
public int getY() {
4956
return y;
5057
}
5158

59+
// Lấy loại đối thoại của hiệu ứng
5260
public int getType() {
5361
return type;
5462
}
5563

64+
// Kiểm tra hiệu ứng có đang hoạt động không
5665
public boolean isActive() {
5766
return active;
5867
}

src/effects/Rain.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,47 +10,61 @@
1010

1111
public class Rain {
1212

13+
// Mảng các hạt mưa (vị trí của các giọt mưa trên màn hình)
1314
private Point2D.Float[] drops;
15+
// Đối tượng Random để tạo các giá trị ngẫu nhiên cho vị trí và di chuyển của giọt mưa, cái này thì giúp hình ảnh chân thực hơn và các hạt mưa không bị di chuyển theo quy luật.
1416
private Random rand;
17+
// Tốc độ di chuyển của hạt mưa
1518
private float rainSpeed = 1.25f;
19+
// Hình ảnh của hạt mưa (được nạp từ sprite atlas)
1620
private BufferedImage rainParticle;
1721

18-
// Worth knowing, adding particles this way can cost a lot in
19-
// computer power. Disable it if the game lags.
22+
// Tạo hiệu ứng mưa
2023
public Rain() {
2124
rand = new Random();
25+
// Khởi tạo 1000 giọt mưa
2226
drops = new Point2D.Float[1000];
27+
// Nạp hình ảnh của hạt mưa từ file sprite atlas
2328
rainParticle = LoadSave.GetSpriteAtlas(LoadSave.RAIN_PARTICLE);
29+
// Gọi phương thức khởi tạo vị trí cho các giọt mưa
2430
initDrops();
2531
}
2632

33+
// Khởi tạo vị trí ngẫu nhiên cho tất cả các giọt mưa
2734
private void initDrops() {
2835
for (int i = 0; i < drops.length; i++)
2936
drops[i] = getRndPos();
3037
}
3138

39+
// Tạo ra một vị trí ngẫu nhiên trong màn hình cho mỗi giọt mưa
3240
private Point2D.Float getRndPos() {
3341
return new Point2D.Float((int) getNewX(0), rand.nextInt(Game.GAME_HEIGHT));
3442
}
3543

44+
// Cập nhật vị trí của các giọt mưa khi chúng rơi xuống
45+
// Mỗi giọt mưa sẽ rơi xuống với tốc độ cố định và được tái sinh ở phía trên cùng khi ra khỏi màn hình
3646
public void update(int xLvlOffset) {
3747
for (Point2D.Float p : drops) {
38-
p.y += rainSpeed;
48+
p.y += rainSpeed; // Di chuyển giọt mưa xuống dưới theo trục y
3949
if (p.y >= Game.GAME_HEIGHT) {
50+
// Khi giọt mưa đi quá chiều cao của màn hình, tái sinh ở trên cùng với vị trí x mới
4051
p.y = -20;
4152
p.x = getNewX(xLvlOffset);
4253
}
4354
}
4455
}
4556

57+
// Tính toán vị trí x mới cho giọt mưa, với giá trị ngẫu nhiên từ ngoài màn hình vào trong màn hình
4658
private float getNewX(int xLvlOffset) {
4759
float value = (-Game.GAME_WIDTH) + rand.nextInt((int) (Game.GAME_WIDTH * 3f)) + xLvlOffset;
4860
return value;
4961
}
5062

63+
// Vẽ các giọt mưa lên màn hình
5164
public void draw(Graphics g, int xLvlOffset) {
5265
for (Point2D.Float p : drops)
53-
g.drawImage(rainParticle, (int) p.getX() - xLvlOffset, (int) p.getY(), 3, 12, null);
66+
g.drawImage(rainParticle, (int) p.getX() - xLvlOffset, (int) p.getY(), 3, 12, null);
67+
// Vẽ giọt mưa với kích thước 3x12 pixel
5468
}
5569

5670
}

0 commit comments

Comments
 (0)