Làm Game Siêu Xịn Bằng Java Phần 1 | Copy Paste Tool

Chào các bạn. Ở những bài viết trước, chúng mình đã chia sẻ với các bạn hướng dẫn làm game với Untity, Cocos2d-x, … Vậy bạn có tò mò nếu chúng ta thử lập trình game với chính những phần mềm quen thuộc như NetBeans hay Eclipse như thế nào không?

Ngày hôm nay, mình sẽ cùng các bạn tìm hiểu về lập trình game trên NetBeans với ngôn ngữ Java cùng game Pikachu – một game rất quen thuộc với tuổi thơ chúng ta mà chúng mình đã có bài viết hướng dẫn code game bằng C++. Các bạn cùng mình tìm hiểu nhé.

*Lưu ý nhé: Ở bài viết mình hướng dẫn các bạn làm với Netbean, trên thực tế, bạn hoàn toàn có thể dùng với bất kỳ IDE nào khác như Eclipse.

1. NetBeans là gì?

NetBeans là một môi trường phát triển tích hợp (IDE) cho Java. NetBeans IDE là một công cụ hỗ trợ lập trình viết mã code miễn phí được cho là tốt nhất hiện nay, được sử dụng chủ yếu cho các lập trình viên phát triển Java tuy nhiên phần mềm có dung lượng khá là nặng dành cho các máy cấu hình có RAM, CPU tương đối cao để vận hành.

NetBeans IDE là môi trường phát triển tích hợp và cực kỳ cần thiết cho các lập trình viên, công cụ này có thể hoạt động tốt với rất nhiều nền tảng hệ điều hành khác nhau như Linux, Windows, MacOS,… là một mã nguồn mở cung cấp các tính năng cần thiết nhất nhằm tạo ra các ứng dụng web, thiết bị di động, desktop.

NetBeans IDE hỗ trợ rất nhiều những ngôn ngữ lập trình như Python, Ruby, JavaScript, Groovy, C / C + +, và PHP.

Lý do Netbeans được dùng rộng rãi là bởi vì cùng với Eclipse, đây là 2 IDE mạnh nhất dùng để lập trình Java cùng nhiều hỗ trợ, và mình tin rằng các bạn học Java đều đã làm quen với 2 phần mềm này. Đối với các bạn muốn học Java, thì Netbeans cũng là một phần mềm dễ làm quen, dễ sử dụng và có rất nhiều hướng dẫn trên Youtube, Google,.. Và tin mình đi, làm quen với nó rồi bạn sẽ bị “yêu” nó luôn và không muốn chuyển qua lập trình Java với phần mềm khác đâu 😀

Tìm hiểu thêm về Netbean tại đây.

2. Hướng dẫn tải và cài đặt Netbeans.

Trước tiên, chúng ta cần cài đặt Java SE Development Kit (JDK) trước mới cài đặt được Netbeans.

Tải về JDK tại đây.

Tải về NetBeans tại đây.

Xem hướng dẫn cài đặt chi tiết tại đây.

3. Tạo Project mới trong Netbean

Sau khi cài đặt xong Netbean cùng môi trường, chúng ta sẽ tạo project mới bằng cách vào File -> New Project hoặc ấn vào phím tắt trên thanh chức năng hoặc dùng tổ hợp phím Ctrl+ Shift+N.

Untitled

Tại màn hình giao diện hiện ra, chọn Java -> Java Application rồi ấn Next>, sau đó, đặt tên cho Project và ấn Finish.

Untitled1

Vậy là đã xong phần cài đặt môi trường và tạo được 1 Project mới rồi, sau đây, chúng ta sẽ cùng tìm hiểu về cách lập trình game Pikachu bằng Java cùng NetBean nha ^^

4. Xây dựng giao diện của game Pikachu

Đối với NetBeans, đây là một phần mềm hỗ trợ tạo giao diện của một ứng dụng Java rất dễ dàng với việc hỗ trợ thiết kế trực tiếp bằng việc kéo thả các Icon trong Java Swing với phần Design của JFrame, JPanel. Đối với các bạn quen dùng Visual Studio, thì phần thiết kế của NetBeans tương tự với phần Design trong Visual Studio. Tuy nhiên ở bài viết này, mình sẽ dùng cách Code thay vì Design bằng việc kéo thả, để các bạn có thể áp dụng trong phần mềm bất kỳ dùng để code Java . Bắt đầu thôi ^^

Chúng ta hãy cùng xem qua màn hình của game Pikachu nguyên bản.

pikachu tmt

Tại đây, chúng ta có thể thấy màn hình chính chia làm 2 phần, 1 là màn hình chứa các ảnh(icon) của các nhân vật đáng yêu trong Pokemon, phần còn lại ở phía trên là thanh điểm cùng thanh thời gian. Hôm nay chúng ta sẽ tìm hiểu về cách tạo ra các Icon của trò chơi và hiển thị chúng lên một cách ngẫu nhiên.

Để có thể tạo được giao diện như trên, đầu tiên, mình tạo packpage icon trong phần Source Packpages của Project vừa tạo, tải hết tất cả các icon lên và đặt tên cho chúng là các số được đánh số từ 1 đến hết. Lý do thì.. Các bạn hãy đọc tiếp để biết nhé ^^

Untitled2

4.1. Xử lý thuật toán đối với các Icon

Vấn đề cần xử lý với các Icon ở đây là: Làm sao để chúng hiển thị lên được đầy đủ, ngẫu nhiên và số các Icon giống nhau luôn là chẵn?

Để xử lý vấn đề này,đầu tiên, mình tạo packpage controller và class Controller để xử lý những thuật toán của game. Mình coi mỗi Icon là 1 Point với 2 tọa độ x,y và tạo ra một ma trận matrix[row][col] với row,col là số hàng và cột của các Icon được tạo ra. Mục đích của việc tạo ra ma trận này là để tạo ra một phiên bản thu nhỏ Game Pikachu của chúng ta, chỉ là thay các Icon bằng các con số. Mỗi Icon sẽ là một con số riêng, hai con số giống nhau chính là hai Icon giống nhau. Bằng việc tạo ra ma trận này, chúng ta có thể xử lý những thuật toán của game gián tiếp qua các con số và vị trí của chúng trong ma trận. Các bạn có thể xem hình ảnh dưới đây để dễ tưởng tượng hơn.

Capture

Từ bây giờ, chúng ta sẽ coi như các Icon xuất hiện trong game cũng giống như các con số xuất hiện trong ma trận. Để các phần tử trong matrix nhận giá trị ngẫu nhiên, mình dùng lớp Random để tạo ra các số ngẫu nhiên. Ở trong phần code của mình, mình có 21 icon , nên mình tạo ra một biến imgCount = 21 và 1 biến index được chọn ngẫu nhiên trong các số từ 1 đến 21 với câu lệnh int index = copypastetool.comInt(imgCount) + 1; Đây là giá trị của các ô trong ma trận mình tạo ra, và đồng thời tương ứng là giá trị của các Icon dùng để so sánh với nhau.

int index = copypastetool.comInt(imgCount) + 1;

Vậy còn vị trí của các Icon hiển thị lên thì sao?

Ở bài toán này, mình tạo ra một ArrayList<Point> listPoint và thêm vào listPoint các Point với vị trí tương ứng với từng ô trong ma trận bằng 2 vòng for cùng 2 biến i,j với i chạy từ 0 đến row -1 và j chạy từ 0 đến col – 1. Khi đó, listPoint sẽ có số phần tử bằng với số phần tử của ma trận matrix.

ArrayList<Point> listPoint = new ArrayList<Point>(); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { copypastetool.com(new Point(i, j)); } }

Sau đó, chúng ta sẽ dùng 1 biến pointIndex được lấy giá trị random theo giá trị size() của listPoint. Với mỗi pointIndex được lấy ra, chúng ta gán giá trị của matrix[pointIndex.x][pointIndex.y] = index; đồng thời remove giá trị pointIndex này ra khỏi listPoint .Việc remove này giúp chúng ta bỏ qua những ô đã được gán giá trị trong ma trận, tránh việc vị trí các ô được lấy qua hàm random trùng lặp nhau, qua đó tất cả các ô sẽ đều được nhận 1 giá trị ngẫu nhiên và không bỏ qua 1 ô nào cả.

int size = copypastetool.com(); int pointIndex = copypastetool.comInt(size); matrix[listPoint.get(pointIndex).x][listPoint.get(pointIndex).y] = index; copypastetool.comve(pointIndex);

Bằng cách này, chúng ta sẽ khởi tạo được vị trí ngẫu nhiên của các Icon game được hiển thị ra, và đồng thời đảm bảo tất cả các phần tử của matrix đều nhận những giá trị ngẫu nhiên.

Tuy nhiên, còn một vấn đề nữa với các Icon mà chúng ta phải giải quyết, đó là làm sao để số các Icon giống nhau luôn là chẵn? Tại sao chúng lại phải chẵn? Chúng ta đều từng chơi Pokemon rồi, 1 cặp Pokemon giống nhau nếu thỏa mãn về đường đi sẽ biến mất. Vậy nếu tất cả đều là ngẫu nhiên, các Icon hiển thị ra bị lẻ, thì chẳng phải game của chúng ta sẽ không thể thắng được sao?

Để giải quyết điều này, mình đặt tất cả quá trình gán giá trị ngẫu nhiên cho các phần tử trong matrix trong 1 vòng for chạy 2 lần. Các bạn hãy theo dõi đoạn code sau:

for (int j = 0; j < 2; j++) { int size = copypastetool.com(); int pointIndex = copypastetool.comInt(size); matrix[listPoint.get(pointIndex).x][listPoint.get(pointIndex).y] = index; copypastetool.comve(pointIndex); }

Khi đặt quá trình gán giá trị ngẫu nhiên cho các phần tử trong matrix trong vòng for ấy, mỗi khi 1 icon được tạo ra với giá trị tương ứng là index trong matrix, sẽ luôn có 1 Icon nữa có giá trị cũng bằng index ở 1 vị trí khác. Nói cách khác, khi đó, chúng ta sẽ không bao giờ tạo ra các Icon đơn lẻ, mà luôn tạo ra các cặp icon giống nhau. Vấn đề hoàn toàn được giải quyết.

Còn một vấn đề nữa đối với việc khởi tạo giá trị của các icon theo ma trận matrix, đó là vì tất cả đều ngẫu nhiên, cho nên sẽ có trường hợp các icon bị trùng lặp quá nhiều, có khi cả trò chơi có 100 ô thì đến 50 ô đều là 1 icon giống nhau thì… Bạn biết đấy, sẽ nhàm chán lắm. Để giải quyết vấn đề này, mình quan tâm đến 1 giá trị là row*col/imgCount. Tại sao lại là giá trị này? Chúng ta hãy tưởng tượng 1 ma trận với kích cỡ là row*col được lấp đầy bằng tổng số Icon là imgCount, nghĩa là trung bình mỗi Icon sẽ xuất hiện là row*col/imgCount lần. Kết hợp với việc số lần mà 1 Icon xuất hiện luôn phải là chẵn, mình rút ra số lần tối đa mà 1 Icon xuất hiện sẽ là giá trị chẵn giữa row*col/imgCount+1 hoặc row*col/imgCount+2.

Đối với bài toán của mình, mình có 21 Icon và mình muốn tạo ra 1 bảng 8×8, nên số lần tối đa mà 1 Icon xuất hiện của mình là 8*8/21+1=4 . Sau khi tính được giá trị này, mình tạo ra 1 mảng int []arr với các giá trị khởi tạo đều là 0 và một biến max=4. Sau đó với mỗi giá trị index được tạo ra ngẫu nhiên, mình cộng thêm 2 vào giá trị của arr[index] (Bởi vì mỗi Index của mình đều đặt trong vòng for chạy 2 lần nên luôn có 2 Icon xuất hiện cùng lúc ứng với mỗi giá trị index). Chúng ta sẽ kiểm tra nếu arr[Index] >= max , Icon ứng với giá trị của index đó sẽ không được tiếp tục tạo ra nữa. Khi đó, mỗi giá trị index ứng với mỗi Icon được tạo ra luôn được giới hạn , như trường hợp của mình, một Icon sẽ xuất hiện tối đa 4 lần.

Random rand = new Random(); int imgCount = 21; int max = 4; int []arr = new int[imgCount + 1]; … int i = 0; do { int index = copypastetool.comInt(imgCount) + 1; if (arr[index] < max) { arr[index] += 2; for (int j = 0; j < 2; j++) { int size = copypastetool.com(); int pointIndex = copypastetool.comInt(size); matrix[listPoint.get(pointIndex).x] [listPoint.get(pointIndex).y] = index; copypastetool.comve(pointIndex); } i++; } } while (i < row * col / 2);

Tất cả phần khởi tạo ở trên mình đặt trong 1 hàm while với biến i nhận giá trị ban đầu là 0. Với mỗi lần khởi tạo ra giá trị cho 2 ô mới của ma trận, câu lệnh i++ sẽ được chạy, và việc khởi tạo giá trị của các ô sẽ kết thúc khi i=row*col/2 ,cũng là khi chúng ta đã tạo đủ giá trị cho tất cả các ô trong ma trận của mình.

Từ đó, chúng ta đã hoàn chỉnh được thuật toán dùng để khởi tạo ma trận của các icon. Đây là toàn bộ phần code của mình, các bạn có thể xem để tham khảo nhé.

package controller; import copypastetool.comt; import copypastetool.comyList; import copypastetool.comom; public class Controller { private int row; private int col; private int[][] matrix; public Controller(int row, int col) { copypastetool.com = row; copypastetool.com = col; copypastetool.comtln(row + “,” + col); createMatrix(); showMatrix(); } public void showMatrix() { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { copypastetool.comtf(“%3d”, matrix[i][j]); } copypastetool.comtln(); } } private void createMatrix() { matrix = new int[row][col]; Random rand = new Random(); int imgCount = 21; int max = 4; int arr[] = new int[imgCount + 1]; ArrayList<Point> listPoint = new ArrayList<Point>(); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { copypastetool.com(new Point(i, j)); } } int i = 0; do { int index = copypastetool.comInt(imgCount) + 1; if (arr[index] < max) { arr[index] += 2; for (int j = 0; j < 2; j++) { int size = copypastetool.com(); int pointIndex = copypastetool.comInt(size); matrix[listPoint.get(pointIndex).x] [listPoint.get(pointIndex).y] = index; copypastetool.comve(pointIndex); } i++; } } while (i < row * col / 2); } public int getRow() { return row; } public void setRow(int row) { copypastetool.com = row; } public int getCol() { return col; } public void setCol(int col) { copypastetool.com = col; } public int[][] getMatrix() { return matrix; } public void setMatrix(int[][] matrix) { copypastetool.comix = matrix; } }

Có 1 số thứ trong thuật toán tạo ma trận này sẽ thay đổi ở phần sau, đó là việc row col khởi tạo ra đều được cộng thêm 2, và ma trận khi đó được bao bọc xung quanh bằng các giá trị bằng 0. Những phần này liên quan đến thuật toán tìm đường đi giữa 2 icon giống nhau mà mình sẽ giải thích ở phần sau ,các bạn hãy đón đọc nhé!

Phần này có vẻ hơi dài đúng không? Đừng hoảng sợ hay lo lắng nhé, vì đây là phần thuật toán chính của bài toán, và chúng ta đã hoàn thành thuật toán dùng để khởi tạo các icon rồi đó. Giờ chúng ta sẽ đến với phần hiển thị nó lên và khởi tạo màn hình game nha. Phần này sẽ dễ và quen thuộc hơn nhiều. Bắt đầu nào!

4.2. Thêm các Icon vào khối để hiển thị

Để có thể tạo ra được các icon, mình tạo 1 class mới là ButtonEvent extends từ JPanel và implements từ ActionListener trong packpage controller để xử lý event của các button được tạo ra, chính là các icon của chúng ta.Mình viết 1 hàm getIcon(int index) để lấy ra các icon theo index (vị trí) của chúng. Ở đây, mình dùng thư viện Image để lấy ra ảnh đã được lưu trong packpage icon, rồi sau đó dùng thư viện Icon để tạo ra các icon với ảnh được lấy ra cùng kích cỡ width, height có thể tùy chỉnh. Việc lấy các ảnh ra bằng index, chúng ta dùng câu lệnh

Image image = new ImageIcon(getClass().getResource( “/icon/” + index + “.png”)).getImage();

Ý nghĩa của câu lệnh là chúng ta sẽ lấy ra ảnh có tên là index trong packpage icon, đó là lý do mà chúng ta đặt tên các icon là các số từ 1 đến hết mà mình đã nói từ đầu đó ^^

Sau khi lấy ra được các icon rồi, mình viết hàm addArrayButton() thực hiện việc tạo ra các button trong bảng, và setIcon cho các button được tạo ra. Chúng ta cần tạo ra đủ số button tương ứng với kích cỡ của bảng, và các icon cho mỗi button được lấy ra bằng hàm getIcon(int index) với giá trị index chính là giá trị tương ứng của ma trận tại vị trí của button đó. Bằng cách này, ma trận của chúng ta bây giờ chính thức là 1 game thu nhỏ của game Pikachu, giúp ta có thể xử lý rất nhiều thứ về thuật toán của game tại ma trận này.

Để hoàn thiện phần ButtonEvent để hiển thị lên các icon của game, mình bổ sung thêm 1 hàm createButton(String action) để tạo sự kiện cho các icon trong các Button mà mình tạo ra. Dưới đây là phần code của class ButtonEvent của mình.

package controller; import copypastetool.comr; import copypastetool.comnsion; import copypastetool.comLayout; import copypastetool.come; import copypastetool.comonEvent; import copypastetool.comonListener; import copypastetool.com; import copypastetool.comeIcon; import copypastetool.comton; import copypastetool.comel; import copypastetool.comyBorder; public class ButtonEvent extends JPanel implements ActionListener { private int row; private int col; private int bound = 2; private int size = 50; private JButton[][] btn; private Controller controller; private Color backGroundColor = copypastetool.comtGray; private MainFrame frame; public ButtonEvent(MainFrame frame, int row, int col) { copypastetool.com = frame; copypastetool.com = row; copypastetool.com = col; setLayout(new GridLayout(row, col, bound, bound)); setBackground(backGroundColor); setPreferredSize(new Dimension((size + bound) * col, (size + bound) * row)); setBorder(new EmptyBorder(10, 10, 10, 10)); setAlignmentY(JPanel.CENTER_ALIGNMENT); newGame(); } public void newGame() { controller = new Controller(this.row, copypastetool.com); addArrayButton(); } private void addArrayButton() { btn = new JButton[row][col]; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { btn[i][j] = createButton(i + “,” + j); Icon icon = getIcon(controller.getMatrix()[i][j]); btn[i][j].setIcon(icon); add(btn[i][j]); } } } private Icon getIcon(int index) { int width = 48, height = 48; Image image = new ImageIcon(getClass().getResource( “/icon/” + index + “.png”)).getImage(); Icon icon = new ImageIcon(image.getScaledInstance(width, height, copypastetool.comE_SMOOTH)); return icon; } private JButton createButton(String action) { JButton btn = new JButton(); copypastetool.comctionCommand(action); copypastetool.comorder(null); copypastetool.comctionListener(this); return btn; } @Override public void actionPerformed(ActionEvent e) { } }

setLayout(new GridLayout(row, col, bound, bound)); Set Layout cho khối JPanel được tạo ra bằng GridLayout (sắp xếp các component thành dạng bảng) với các tham số hàng, cột và khoảng cách giữa các component theo chiều ngang và chiều dọc bằng bound=2.

setBackground(backGroundColor); Cài đặt màu nền cho khối JPanel.

setPreferredSize(new Dimension((size + bound) * col, (size + bound)* row)); Cài đặt kích cỡ của khối Panel.

setBorder(new EmptyBorder(10, 10, 10, 10)); Thêm viền bên ngoài của khối Panel.

4.3. Chạy chương trình và hiển thị các Icon

Sau khi hoàn thành 2 phần trên, mình tạo class MainFrame extends từ JFrame trong packpage controller để làm màn hình chính của game. Trong class MainFrame này, mình tạo 2 hàm createMainPanel()createGraphicsPanel() để thêm vào các Button chứa các icon của game.

package controller; import copypastetool.comerLayout; import copypastetool.comr; import copypastetool.comBagLayout; import copypastetool.comonEvent; import copypastetool.comonListener; import copypastetool.comme; import copypastetool.comel; public class MainFrame extends JFrame implements ActionListener, Runnable { private int row = 8; private int col = 8; private int width = 700; private int height = 500; private ButtonEvent graphicsPanel; private JPanel mainPanel; public MainFrame() { add(mainPanel = createMainPanel()); setTitle(“Pokemon Game”); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(width, height); setLocationRelativeTo(null); setVisible(true); } private JPanel createMainPanel() { JPanel panel = new JPanel(new BorderLayout()); copypastetool.com(createGraphicsPanel(), copypastetool.comER); return panel; } private JPanel createGraphicsPanel() { graphicsPanel = new ButtonEvent(this, row, col); JPanel panel = new JPanel(new GridBagLayout()); copypastetool.comackground(Color.gray); copypastetool.com(graphicsPanel); return panel; } @Override public void actionPerformed(ActionEvent e) { } @Override public void run() { } }

setTitle(“Pokemon Game”); Đặt tiêu đề cho Frame.

setResizable(false); Cài đặt không cho phép phóng to màn hình.

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Cài đặt tắt chương trình khi ấn vào dấu X ở góc phải.

setSize(width, height); Cài đặt kích cỡ của Frame hiển thị.

setLocationRelativeTo(null); Chương trình khi chạy sẽ hiển thị ở chính giữa màn hình.

setVisible(true); Hiển thị Frame.

Cuối cùng, chúng ta viết hàm Main để chạy chương trình.

package controller; public class Main { MainFrame frame; public Main() { frame = new MainFrame(); } public static void main(String[] args) { new Main(); } }

Sau khi hoàn thành hàm main, chương trình sẽ hiển thị được như sau:

Untitled3

Tạm kết

Như vậy, chúng ta đã hoàn thành việc hiển thị các Icon lên màn hình, và hoàn thành thuật toán để các Icon được hiển thị đầy đủ, ngẫu nhiên và số cặp Icon giống nhau luôn là chẵn.

Ở bài viết sau, chúng ta sẽ cùng hoàn thiện giao diện game và làm những thao tác cơ bản nhất với ma trận mà chúng ta đã tạo ra nhé! Cảm ơn các bạn đã đọc bài viết của mình. Mọi ý kiến, thắc mắc các bạn hãy để lại ở phần Comment nhé. Mình sẽ đọc và rep đầy đủ ^^ See ya ~~

Tham khảo:

– Code của mình và hình ảnh mình sử dụng: copypastetool.com/beloyten/daoluongduy-gmail.com

Viết một bình luận