Skip to content

Commit

Permalink
Merge branch 'ppap-1264589:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
CptDat9 authored Oct 13, 2024
2 parents 5898a76 + 7e40567 commit c9b60c8
Show file tree
Hide file tree
Showing 21 changed files with 453 additions and 71 deletions.
12 changes: 12 additions & 0 deletions .classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="src/|res/" kind="src" path=""/>
<classpathentry kind="src" path="res"/>
<classpathentry kind="output" path="bin"/>
</classpath>
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bin/
17 changes: 17 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SparrowAdventure</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
# Bài tập lớn Lập trình Hướng đối tượng (OOP)


<p align="center">
<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>
</p>
<div align="center">
<img alt="Static Badge" src="https://img.shields.io/badge/HUST-course-blue?labelColor=EE4E4E&color=151515">
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/ppap-1264589/SparrowAdventure?labelColor=7AA2E3&color=97E7E1">
</div>

## Đây là phần code bài tập lớn OOP lớp thầy Trần Nhật Hóa
## Giới thiệu
"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.
## Thành viên
- Đỗ Xuân Hoàng
- Đoàn Xuân Công Đạt
- Nguyễn Tùng Dương
- Nguyễn Trung Hiếu
- Nguyễn Hoài Phương

## Người đóng góp

<div align="center">
<a href="https://github.com/ppap-1264589/SparrowAdventure/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ppap-1264589/SparrowAdventure" />
</a>
</div>

## Tính năng

## Cài đặt và triển khai


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

https://github.com/KaarinGaming/PlatformerTutorial/tree/ep29

## Giải thích
## Minh họa
Binary file added res/button_atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/menu_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
161 changes: 161 additions & 0 deletions src/audio/AudioPlayer.java
Original file line number Diff line number Diff line change
@@ -1 +1,162 @@
package audio;

import java.io.IOException;
import java.net.URL;
import java.util.Random;
import javax.sound.sampled.*;

public class AudioPlayer {

// Các chỉ số cho các bài nhạc
public static int MENU_1 = 0;
public static int LEVEL_1 = 1;
public static int LEVEL_2 = 2;

// Các chỉ số cho các hiệu ứng âm thanh
public static int DIE = 0; //Chết
public static int JUMP = 1;// Nhảy
public static int GAMEOVER = 2;// Kết thúc trò chơi
public static int LVL_COMPLETED = 3;// hoàn thành level
public static int ATTACK_ONE = 4;// Kĩ năng đánh 1
public static int ATTACK_TWO = 5;// Kĩ năng đánh 2
public static int ATTACK_THREE = 6;// Kĩ năng đánh 3

private Clip[] songs, effects; // Mảng chứa các bài nhạc và hiệu ứng âm thanh
private int currentSongId; // ID bài nhạc đang phát
private float volume = 0.5f; // Mức âm lượng mặc định
private boolean songMute, effectMute; // Trạng thái tắt tiếng của nhạc và hiệu ứng
private Random rand = new Random(); // Để phát hiệu ứng âm thanh ngẫu nhiên

// Constructor: Khởi tạo AudioPlayer và bắt đầu phát nhạc menu
public AudioPlayer() {
loadSongs(); // Nạp các bài nhạc
loadEffects(); // Nạp các hiệu ứng âm thanh
playSong(MENU_1); // Phát nhạc menu
}

// Nạp các file âm thanh cho nhạc nền từ thư mục /audio/
private void loadSongs() {
String[] names = { "menu", "level1", "level2" }; // Tên các file nhạc
songs = new Clip[names.length]; // Tạo mảng chứa các bài nhạc
for (int i = 0; i < songs.length; i++)
songs[i] = getClip(names[i]); // Lấy từng bài nhạc từ file
}

// Nạp các file âm thanh cho hiệu ứng từ thư mục /audio/
private void loadEffects() {
String[] effectNames = { "die", "jump", "gameover", "lvlcompleted", "attack1", "attack2", "attack3" }; // Tên các file hiệu ứng
effects = new Clip[effectNames.length]; // Tạo mảng chứa hiệu ứng âm thanh
for (int i = 0; i < effects.length; i++)
effects[i] = getClip(effectNames[i]); // Lấy từng hiệu ứng từ file

updateEffectsVolume(); // Cập nhật âm lượng cho hiệu ứng âm thanh
}

// Lấy file âm thanh từ đường dẫn và tạo đối tượng Clip
private Clip getClip(String name) {
URL url = getClass().getResource("/audio/" + name + ".wav"); // Đường dẫn tới file âm thanh
AudioInputStream audio;

try {
audio = AudioSystem.getAudioInputStream(url); // Lấy dữ liệu âm thanh từ file
Clip c = AudioSystem.getClip(); // Tạo đối tượng Clip
c.open(audio); // Mở file âm thanh
return c; // Trả về đối tượng Clip

} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace(); // Bắt và in lỗi nếu có
}

return null; // Trả về null nếu xảy ra lỗi
}

// Đặt âm lượng mới cho cả nhạc nền và hiệu ứng âm thanh
public void setVolume(float volume) {
this.volume = volume;
updateSongVolume(); // Cập nhật âm lượng của nhạc
updateEffectsVolume(); // Cập nhật âm lượng của hiệu ứng
}

// Dừng phát nhạc hiện tại
public void stopSong() {
if (songs[currentSongId].isActive())
songs[currentSongId].stop(); // Dừng bài nhạc nếu nó đang phát
}

// Đặt nhạc nền theo cấp độ (level) trò chơi
public void setLevelSong(int lvlIndex) {
if (lvlIndex % 2 == 0)
playSong(LEVEL_1); // Phát nhạc của level 1 nếu chỉ số là chẵn
else
playSong(LEVEL_2); // Phát nhạc của level 2 nếu chỉ số là lẻ
}

// Khi hoàn thành màn chơi, dừng nhạc và phát hiệu ứng hoàn thành
public void lvlCompleted() {
stopSong();
playEffect(LVL_COMPLETED); // Phát hiệu ứng âm thanh hoàn thành level
}

// Phát hiệu ứng âm thanh tấn công ngẫu nhiên
public void playAttackSound() {
int start = 4; // Các hiệu ứng tấn công bắt đầu từ chỉ số 4
start += rand.nextInt(3); // Chọn ngẫu nhiên giữa các hiệu ứng tấn công
playEffect(start); // Phát hiệu ứng tấn công
}

// Phát hiệu ứng âm thanh
public void playEffect(int effect) {
if (effects[effect].getMicrosecondPosition() > 0)
effects[effect].setMicrosecondPosition(0); // Reset thời gian nếu hiệu ứng đã phát
effects[effect].start(); // Bắt đầu phát hiệu ứng
}

// Phát nhạc nền
public void playSong(int song) {
stopSong();
// Dừng bài nhạc hiện tại

currentSongId = song; // Cập nhật ID bài nhạc mới
updateSongVolume(); // Cập nhật âm lượng
songs[currentSongId].setMicrosecondPosition(0); // Đặt lại vị trí phát nhạc
songs[currentSongId].loop(Clip.LOOP_CONTINUOUSLY); // Phát nhạc liên tục
}

// Chuyển đổi trạng thái tắt/bật tiếng của nhạc nền
public void toggleSongMute() {
this.songMute = !songMute;
for (Clip c : songs) {
BooleanControl booleanControl = (BooleanControl) c.getControl(BooleanControl.Type.MUTE);
booleanControl.setValue(songMute); // Tắt hoặc bật tiếng cho nhạc
}
}

// Chuyển đổi trạng thái tắt/bật tiếng của hiệu ứng âm thanh
public void toggleEffectMute() {
this.effectMute = !effectMute;
for (Clip c : effects) {
BooleanControl booleanControl = (BooleanControl) c.getControl(BooleanControl.Type.MUTE);
booleanControl.setValue(effectMute); // Tắt hoặc bật tiếng cho hiệu ứng
}
if (!effectMute)
playEffect(JUMP); // Nếu bật lại tiếng, phát hiệu ứng nhảy
}

// Cập nhật âm lượng cho nhạc nền
private void updateSongVolume() {
FloatControl gainControl = (FloatControl) songs[currentSongId].getControl(FloatControl.Type.MASTER_GAIN);
float range = gainControl.getMaximum() - gainControl.getMinimum(); // Tính khoảng điều chỉnh âm lượng
float gain = (range * volume) + gainControl.getMinimum(); // Tính giá trị âm lượng mới
gainControl.setValue(gain); // Đặt âm lượng mới cho nhạc nền
}

// Cập nhật âm lượng cho hiệu ứng âm thanh
private void updateEffectsVolume() {
for (Clip c : effects) {
FloatControl gainControl = (FloatControl) c.getControl(FloatControl.Type.MASTER_GAIN);
float range = gainControl.getMaximum() - gainControl.getMinimum(); // Tính khoảng điều chỉnh âm lượng
float gain = (range * volume) + gainControl.getMinimum(); // Tính giá trị âm lượng mới
gainControl.setValue(gain); // Đặt âm lượng mới cho hiệu ứng âm thanh
}
}
}
35 changes: 22 additions & 13 deletions src/effects/DialogueEffect.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,67 @@
package effects;

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

public class DialogueEffect {

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

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

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

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

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

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

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

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

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

// Kiểm tra hiệu ứng có đang hoạt động không
public boolean isActive() {
return active;
}
Expand Down
22 changes: 18 additions & 4 deletions src/effects/Rain.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,61 @@

public class Rain {

// Mảng các hạt mưa (vị trí của các giọt mưa trên màn hình)
private Point2D.Float[] drops;
// Đố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.
private Random rand;
// Tốc độ di chuyển của hạt mưa
private float rainSpeed = 1.25f;
// Hình ảnh của hạt mưa (được nạp từ sprite atlas)
private BufferedImage rainParticle;

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

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

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

// Cập nhật vị trí của các giọt mưa khi chúng rơi xuống
// 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
public void update(int xLvlOffset) {
for (Point2D.Float p : drops) {
p.y += rainSpeed;
p.y += rainSpeed; // Di chuyển giọt mưa xuống dưới theo trục y
if (p.y >= Game.GAME_HEIGHT) {
// 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
p.y = -20;
p.x = getNewX(xLvlOffset);
}
}
}

// 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
private float getNewX(int xLvlOffset) {
float value = (-Game.GAME_WIDTH) + rand.nextInt((int) (Game.GAME_WIDTH * 3f)) + xLvlOffset;
return value;
}

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

}
Loading

0 comments on commit c9b60c8

Please sign in to comment.