Side project: Code2Image

Chào mọi người, thật lâu rồi đúng không. Nay rảnh rảnh không có việc gì làm ngó lại thấy cái blog từ hồi sinh viên vẫn có traffic nên hôm nay tôi lên một bài blog vớ vẩn chia sẻ cái tool side project tôi vừa code đợt rồi. Vào bài thôi!!!

Bạn cần một cách nhanh chóng và dễ dàng để chia sẻ các đoạn mã dưới dạng hình ảnh không? Không cần tìm đâu xa, hãy đến với Code2Image, công cụ mới sáng tạo chuyển đổi code thành hình ảnh có thể tùy chỉnh dễ dàng để giúp bạn có thể chèn mấy cái ảnh code của mình vào bất cứ đâu.

🌐 Trang web: https://codetoimage.vercel.app

Với Code2Image, bạn có thể dễ dàng

  • Thay đổi chủ đề - Lựa chọn từ nhiều tông màu và hình nền khác nhau để thể hiện mã của bạn tốt nhất. Có chế độ sáng và tối.
  • Chuyển đổi ngôn ngữ lập trình - Hỗ trợ tất cả các ngôn ngữ chính bao gồm Python, JavaScript, C++ và nhiều ngôn ngữ khác. Tô sáng cú pháp tự động phát hiện ngôn ngữ.
  • Tùy chỉnh phông chữ - Chọn họ phông chữ, kích thước và khoảng đệm để tối ưu hóa khả năng đọc.
  • Xuất dưới dạng PNG hoặc SVG - Tải xuống hình ảnh mã của bạn hoặc lưu chúng vào bảng tạm để dễ dàng chia sẻ.

Công nghệ sử dụng

  1. ReactJS
  2. NextUI
  3. TailwindCSS
  4. Zustand thư viện state management dễ dùng hơn Redux

Một vài hình ảnh







Bài viết có vậy thôi, lâu không viết nên không biết viết gì :))
Bye, hẹn gặp lại các bạn.

Hướng dẫn xóa tất cả file trong thư mục linux ngoại trừ các file được chỉ định

Chào mọi người, lâu lắm rồi hôm nay mới viết blog, chủ nhật rảnh rỗi không có việc gì làm ngồi viết vài dòng blog cho vui :D.

Hôm nay mình muốn hướng dẫn các  bạn cách xóa tất file trong một thư mục nhưng loại trừ ra các file các bạn chỉ định.

Bài viết này mình tham khảo nguồn từ: https://www.ostechnix.com/remove-files-folder-except-one-specific-file-linux/
Nếu các bạn muốn đọc tiếng Anh thì vào luôn trang này xem, có rất nhiều bài viết hay, còn nếu ngại đọc tiếng Anh thì có thể đọc bài viết của mình. (Mình đã xin phép admin trang này rồi nên các bạn yên tâm ^^).

Bài này thì không phải mình dịch lại giống hệt mà mình sẽ hướng dẫn và giải thích theo các hiểu của mình.

Rồi, chúng ta bắt đầu nhé.

Thông thường khi bạn muốn xóa các tất cả file trong một thư mục trên linux thì chỉ cần: rm -r  <đường dẫn thư mục>, hoặc trên giao diện(Chẳng hạn của Nautilus) bạn Ctrl + A xong giữ Ctrl và click chuột trái vào các file muốn giữ lại rồi delete, với thư mục chứa ít file thì ok nhưng nếu nhiều file liệu có ổn không?

Bây giờ ta cùng xem ví dụ dưới đây:
Giả sử ta có thư mục test có 10 file như sau:







Giờ nếu bạn muốn xóa tất cả ngoại trừ file có tên file10.txt thì bạn gõ lệnh như sau trên terminal:
- Đầu tiên: cd test/ để di chuyển vào thư mục test
- Sau đó: rm -f !(file10.txt) hoặc  rm !(file10.txt) để xóa tất cả các file trong thư mục test và để lại file10.txt
Ngoài ra các bạn cũng có thể sử dụng lệnh find để làm việc như trên: find . ! -name file10.txt -delete lệnh này sẽ tìm và xóa tất cả các file ngoại trừ file10.txt 

Thông thường trong một thư mục chúng ta không chỉ chứa mỗi một định dạng file như thế mà chúng ta chứa nhiều file với nhiều định dạng khác nhau, ta cùng nhau xem ví dụ dưới đây:

Giả sử bây giờ thư mục test có các file như sau:
Bây giờ bạn chỉ muốn giữ lại các file có phần mở rộng là *.doc thì bạn sử dụng lệnh như sau: rm !(*.doc), lúc này thư mục test sẽ chỉ còn lại các file có phần mở rộng là *.doc mà thôi, tương tự các bạn làm với các file có phần mở rộng khác.

Giờ bạn lại muốn giữ lại các file có phần mở rộng là *.doc và *.mp3 thì việc này cũng rất đơn giản, bạn chỉ việc chạy lệnh y hệt như trên nhưng phần mở rộng ta sẽ thêm vào và phân cách nhau bởi dấu "|" (Tạm gọi là dấu gạch đứng): rm !(*.doc | *.mp3).

Rất đơn giản và nhanh gọn đúng không các bạn, hy vọng bài viết giúp ích cho các bạn, cám ơn các bạn đã đọc bài viết này.
Xin chào và hẹn gặp lại trong các bài viết sau.

Lấy kích thước chữ trong libGDX

Lấy kích thước chữ trong libGDX

Trích dẫn từ Stackoverflow

BitmapFont API < 1.5.6

To mesure the width of a String you use your Font and get the bounds of the String, you are going to draw.
BitmapFont.getBounds(String str).width
That's all. You can also get the height to get the right offset for drawing like this. Just replace width with height.
You can use getMultiLineBounds(someString).width for multiple line texts.

BitmapFont API >= 1.5.6

The BitmapFont API changed in 1.5.7 so there is a different way to get the bounds now:
BitmapFont.TextBounds and getBounds are done. Instead, give the string to GlyphLayout and get the bounds using its width and height fields. You can then draw the text by passing the same GlyphLayout to BitmapFont, which means the glyphs don’t have to be laid out twice like they used to. src
Example:
GlyphLayout layout = new GlyphLayout(); //dont do this every frame! Store it as member
layout.setText("meow");
float width = layout.width;// contains the width of the current set text
float height = layout.height; // contains the height of the current set text

Tóm lại: kể từ phiên bản libGDX 1.5.6 trở lên thì khi muốn lấy kích thước của 1 đoạn text ta phải sử dụng GlyphLayout (Có vẻ lằng nhằng hơn :D)

Lập trình game với libGDX - Tìm hiểu về Animation




Chào mừng các bạn đã quay trở lại với blog, mấy hôm nay bận dọn phòng để chuẩn bị năm học mới với cả lười nên hôm nay mình mới có thời gian viết bài (thực ra thì 90% là do lười chứ không phải là không có thời gian :v).
Thôi không xàm nữa, chúng ta sẽ cùng điểm cả một số ý chính của bài hôm nay:

  • Tìm hiểu về Animation
  • Cách sử dụng trọng libGDX.
Ok, chúng ta bắt đầu tìm hiểu nhé :D.


I, Tìm hiểu về Animation:

Chắc hẳn ai trong chúng ta đều trải qua 1 tuổi thơ, có thể êm đềm, hoặc cũng có thể rất dữ dội =)), và dù êm đềm hay dữ dội đi nữa thì chắc hẳn phim hoạt hình cũng là một phần không thể thiếu trong tuổi thơ của chúng ta (nói vậy thôi chứ bây giờ lớn vẫn xem :v).
Ủa? Mình có lạc đề không ta =))

Thực ra mình đưa ra ví dụ về phim hoạt hình để cho các bạn dễ dàng liên tưởng nhất đến khái niệm Animation mà thôi.
Như các bạn đã biết, người ta làm phim hoạt hình bằng cách sử dụng nhiều khung hình và cho chạy liên tiếp để thu được các chuyển động của nhân vật. Và trong game thì khái niệm Animation (hoạt ảnh) cũng hoàn toàn tương tự thôi :D

Mình sẽ trích ra tư wiki trên github, khái niệm Animation cực kỳ đơn giản và dễ hiều :D

An animation consists of multiple frames which are shown in a sequence at set intervals.
 Các bạn có thể vào xem trên wiki, bài viết khá cụ thể, có ví dụ đi kèm
https://github.com/libgdx/libgdx/wiki/2D-Animation

Bây giờ chúng ta sẽ cùng nhau đi sang phần tiếp theo.

II, Cách sử dụng Animation trong libGDX:


Để có thể tạo animation thì libGDX cung cấp cho chúng ta một lớp có tên là Animtion, các bạn có thể đọc document của nó tại tại đây, khá đầy đủ và chi tiết:
https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Animation.html

class này bao gồm 3 contructors, nhưng chúng ta sẽ sử dụng contructor thứ 2 (cái có nhiều tham số nhất ấy)

Animation(float frameDuration, Array<? extends TextureRegion> keyFrames)

Như các bạn thấy nó có chứa 2 tham số, ý nghĩa của từng tham số như sau:

  • frameDuration: đây chính là khoảng thời gian nghỉ giữ mỗi khung hình và được tính bằng giây.
  • keyFrames: mảng lưu các khung hình cần hiển thị, ở đây được vào bộ nhớ bởi các đối tượng TextureRegion mà chúng ta đã học ở bài trước.
Như đã thấy ở contructor trên thì chúng ta cần có một mảng các frame cần hiển thị, chúng ta có thể lấy tường bức ảnh đại diện mỗi frame rồi load vào mảng các frames đó, nhưng cách này không tối ưu, và cũng chẳng ai xài cách này đâu.
Nhớ lại bài hôm trước, chúng ta đã học cách sử dugnj Texture Packer để đóng gói ảnh lai đúng không nào, thông thương người ta sẽ dùng Texture Packer để đóng gói các frame nhỏ lại thành một tấm lớn, hoặc có thể dùng trình chỉnh sửa ảnh như Photoshop để làm điều này.

Và cái "bức ảnh lớn" mà mình thu được sau khi đóng gói lại sẽ được gọi là Sptite Sheet nha các bạn.

Rồi, bây giờ để mà test thử thì chúng ta cần có một sprite sheet, các bạn lên google gõ "sprite sheet" ra một đống luôn. Nhưng mình chắc là các bạn sẽ lười nên mình đã chuẩn bị sẵn một tấm ở đây rồi :)).
Ảnh này mình lấy trên github luôn.

Các bạn thấy ở sprite sheet trên thì các frame được sắp xếp giống như các phần tử trên một ma trân vậy, và việc duyệt các frame này cũng y chang việc các bạn duyệt các phần tử của một ma trân thôi. các bạn xem hình sau để hiểu rõ hơn:

Các frame sẽ được duyệt theo dòng từ trái qua phải và trên xuống, nếu sử dụng chế độ lặp lại thì khi chạy đến frame cuối cùng nó sẽ quay về và tiếp tục hiển thị frame đầu.

Bây giờ chúng ta sẽ code để cho nó ra chuyển động nha, việc này khá thú vị :)).
Các bạn nhớ nắm rõ cách thức hoạt động của nó vì animation trong game sử dụng khá nhiều đấy.

Không lan man nữa :)), bây giờ các bạn mở file code ra, rồi code như sau:


package com.blogspot.gameiter;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class libGDXAnimation extends ApplicationAdapter {
 private static final int FRAME_COLS = 6;
 private static final int FRAME_ROWS = 5;

 Animation walkAnimation;
 Texture walkSheet;
 TextureRegion[] walkFrames;
 SpriteBatch spriteBatch;
 TextureRegion currentFrame;

 float stateTime;

 @Override
 public void create() {
  walkSheet = new Texture(Gdx.files.internal("animation.png"));
  TextureRegion[][] tmp = TextureRegion.split(walkSheet,
    walkSheet.getWidth() / FRAME_COLS, walkSheet.getHeight()
      / FRAME_ROWS);
  walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
  int index = 0;
  for (int i = 0; i < FRAME_ROWS; i++) {
   for (int j = 0; j < FRAME_COLS; j++) {
    walkFrames[index++] = tmp[i][j];
   }
  }
  walkAnimation = new Animation(0.025f, walkFrames);
  spriteBatch = new SpriteBatch(); 
  stateTime = 0f; 
 }

 @Override
 public void render() {
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  stateTime += Gdx.graphics.getDeltaTime();
  currentFrame = walkAnimation.getKeyFrame(stateTime, true);
  spriteBatch.begin();
  spriteBatch.draw(currentFrame, 50, 50);
  spriteBatch.end();
 }
}


  • 2 hằng số đầu tiên: FRAME_COLS và FRAME_ROWS chính là số hàng và số cột của sprite sheet, ơ đây bao gồm FRAME_COLS * FRAME_ROWS = 6 * 5 = 30 frame.
  • Tiếp theo là khai báo các đối tượng cần sử dụng, bao gồm: walkAnimation để tạo hoạt ảnh, walkSheet là Texture dùng để load tấm ảnh sprite sheet vào bộ nhớ.
  • walkFrames là mảng lưu các frame sau khi được cắt ra từ walkSheet.
  • currentFrame là frame hiện tại để hiển thị theo mỗi khoảng thời gian.
  • stateTime: tạm gọi là các mốc trạng thái, chúng ta sẽ cần dựa vào nó để lấy ra frame từ mảng frames của chúng ta.
Trong phương thức create() các bạn thấy có một biến tmp, biến này là một mảng 2 chiều có chức năng lưu các frame được cắt ra từ sprite sheet.
Tiếp đó, trong vòng lặp ta sẽ "đổ" lần lượt các frame đã cắt ra vào mảng một chiều walkFrames.
Cuối cùng chúng ta tạo mới đối tượng Animation với hai tham số như đã nói lúc đầu và gán stateTime lúc đầu bằng 0.

Trong phương thức render() thì chúng ta sẽ cộng vào stateTime một khoảng thời gian bằng thời gian nghỉ giữa các frame.
Sau đó gán currentFrame với frame tương ứng stateTime bằng phương thức getKeyFrame(float stateTime, boolean looping), tham số thứ 2 biểu diễn việc lặp lại frame hay không.
Cuối cùng ta vẽ currentFrame lên màn hình, do currentFrame liên tục thay đổi nên ta sẽ thấy được chuyển động của nhân vật.
Kêt quả chạy lên như sau:


Cũng không mấy khó khăn đúng không các bạn :D.
Chúc các bạn thành công, bài hôm nay mình xin dừng ở đây, hẹn gặp lại các bạn trong các bài sau.

Lập trình game với libGDX - Hướng dẫn sử dụng Texture Packer - TextureAtlas - AtlasRegion

Chào mừng các bạn đã quay trở lại blog :)
Bài hôm trước chúng ta đã tìm hiểu một số đối tượng về đồ họa trong libGDX gồm có: Sprite, Texture, TextureRegion, SpriteBatch.
Bài hôm nay chúng ta sẽ cùng nhau tìm hiểu về một công cụ khá hữu ích đó là Texture Packer. Bây giờ ta sẽ điểm qua một số ý chính mà hôm nay chúng ta sẽ học:


  • Texture Packer là gì? Cách sử dụng?
  • TextureAtlas là gì? Cách sử dụng?



I, Texture Packer là gì? Cách sử dụng?


1, Texture Packer là gì?

Texture Packer đơn giản là một công cụ có khả năng đóng gói các file hình ảnh đơn lẻ lại thành một file hình ảnh bao gồm các ảnh nhỏ đó. Điều đặc biệt là sau khi đóng gói các ảnh vào bức ảnh lớn thì Texture Packer sẽ ghi ra một file text chứ một số thông số quan trọng, vậy các thông số đó là gì? Một lát nữa qua phần hướng dẫn sử dụng mình sẽ nói rõ hơn. Các bạn cứ hình dung như trò chơi ghép tranh vậy đó: mỗi mảnh ghép là một bức ảnh nhỏ, bức ảnh lớn sẽ là bức tranh mà ta ghép được :D.

2, Cách sử dụng Texture Packer:

Đầu tiên để mà sử dụng thì ta cần download nó về đã, các bạn vào link sau để download về nha:
https://code.google.com/archive/p/libgdx-texturepacker-gui/downloads
Các bạn cứ quất bản cao nhất về mà xài, bản 3.2.0 đấy.

Rồi, sau khi down về các bạn giải nén nó ra, sẽ được kết quả như sau:

Một file jar để chạy chương trình và một thư mục demo.
Các bạn chạy file jar đó lên để chạy chương trình:
Giao diện chương trình:

Các bạn nhấn New pack để tao mới.
Nhập tên rồi bấm Ok.

Làm theo hướng dẫn là xong, nói chung giao diện rất dễ sủ dụng :)), mình không cần hướng dẫn nhiều.

Chúng ta sẽ sử dụng thư mục demo này cho ví dụ luôn nha.
Bây giờ các bạn mở thư mục đó lên xem nó có gì trong đó.

Trong này có 2 thư mục là input và output.
Trong thư mục input sẽ chứa các ảnh ảnh mà ta cần đóng gói:


Trong thư mục output sẽ chứa bức ảnh đóng gói các bức ảnh nhỏ, cùng với một file có đuôi .pack (thực ra đuôi này bạn đôi thành .txt hay .abc cũng được, không quan trọng tên đuôi).

Bây giờ ta sẽ cùng nhau xem trong file test-me!.pack kia nó có cái gì trong đó nha:



Các thuộc tính quan trọng mình đã chú thích trong ảnh. Vậy file .pack này có ý nghĩa gì?
Câu trả lời sẽ có khi bạn đọc phần tiếp theo :)).

II, TextureAtlas là gì? Cách sử dụng?

1, TextureAtlas là gì?

Ở phần trên chúng ta đã tìm hiểu Texture Packer, thì output của Texture Packer sẽ là một file ảnh và một file văn bản chứa các thuộc tính của các bức ảnh nhỏ.
Đối tượng Texture Atlas sẽ có chức năng đọc dữ liệu từ file văn bản (ở đây là file .pack đó) rồi load các bức ảnh nhỏ dựa vào dữ liệu đọc được từ tấm ảnh lớn.

2, Cách sử dụng đối tượng TextureAtlas:

Giờ ta tạo mới một project để test thử nha, hoặc nếu lười thì các bạn lấy lại project hôm trước làm luôn cũng được.
Đầu tiên thì các bạn cần copy 2 file output gồm test-me!.pack và test-me!.png vào thư mục assets trong project android đã, sau đó ta sẽ có code như sau:

package com.gameiter.hoclibgdx;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class LearningliGDXb extends ApplicationAdapter {
 private TextureAtlas atlas;
 private SpriteBatch batch;
 private AtlasRegion region;
 @Override
 public void create() {
  batch = new SpriteBatch();
  atlas = new TextureAtlas("test-me!.pack");
  region = atlas.findRegion("test01");
 }
 @Override
 public void render() {
  Gdx.gl.glClearColor(0, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  batch.draw(region, 100, 100);
  batch.end();
 }
}

Các bạn có thấy "quen" không? :))
Đối tượng TextureAtlas khá giống đối tượng Texture và đối tượng AtlasRegion lại giống đối tượng TextureRegion đúng không :)), do đó code trên khá dễ hiểu.
Đầu tiều đối tượng TextureAtlas sẽ load dữ liệu từ file test-me!.pack, trong file này có chứa tên của bức ảnh lớn. Sau đó đối tượng AtlasRegion sẽ load ảnh từ đối tượng TextureAtlas dựa theo tên file (Các tên file cũng được ghi trong file .pack - cái này hồi nãy chúng ta đã tìm hiểu rồi).
Phương thức render() đơn giản là vẽ lên màn hình đối tượng AtlasRegion :)
Kế quả thu được:

Các bạn kiểm tra lại file ảnh test01.png trong thư mục input hồi nãy có đúng ảnh này không nha, tương tự để load các tấm ảnh kia thì ta sẽ thay tên file tương ứng là xong :).

Bài hôm nay đến đây thôi, bài hôm sau ta sẽ tìm hiểu về Animation, cám ơn các bạn đã quan tâm theo dõi, hẹn gặp lại trong các bài viết sau :D

Lập trình game với libGDX - Làm quen với Texture, TextureRegion, Sprite, SpriteBatch - Vẽ hình

Xin chào các bạn, chào mừng các bạn đã quay trở lại với blog trong series về lập trình game với libGDX :D.
Các bài trước chúng ta đã biết được khái niệm Game engine, cách tạo project libGDX, cấu trúc project và life cycle của nó.

Bài học nay chúng ta học gì?

  • Tìm hiểu một số đối tượng quan trọng không thể thiếu trong lập trình game với libGDX
  • Cách vẽ hình trong game.
Ok, bây giờ chúng ta sẽ bắt đầu nha.



I, Tìm hiểu một số khái niệm quan trọng:

1, Sprite là gì?

Chắc hẳn ai trong chúng ta cũng đã từng chơi qua một game, dù là khủng hay củ chuối đi nữa thì đều đã thấy được sprite.
Ồ, vậy sprite là gì?
Sprite đơn giản là một đối tượng hình vẽ được vẽ lên màn hình.

Hình trên là game Flappy Bird một thời làm mưa làm gió trên thế giới game moblie :D, thì trong hình bạn thấy đó, hình ảnh con chim là một sprite, cái ống nước cũng vậy.
Do đó, sprite là thành phần không thể thiếu trong game (Các bạn có thấy game nào không có hình ảnh chưa? :v).

Bất kể bạn lập trình game với cái gì đi nữa thì khái niệm sprite vẫn như vậy :D.

2, Một số khái niệm quan trọng:

Để quan lý sprite trong game thì libGDX sử dụng một số lớp quan trọng sau đây:

  • SpriteBatch
  • Texture
  • TextureRegion
  • Sprite
Giờ ta sẽ đi tìm hiểu chi tiết về các lớp này (Các trích dẫn lấy từ wiki trên github):

a, SpriteBatch:

SpriteBatch is given a texture and coordinates for each rectangle to be drawn. It collects the geometry without submitting it to the GPU. If it is given a texture different than the last texture, then it binds the last texture, submits the collected geometry to be drawn, and begins collecting geometry for the new texture.
Đây là lớp quan trọng có chức năng quản lý việc vẽ hình trong libGDX.
Nó bao gồm 2 phương thức quan trọng là begin() và end(). Bạn tưởng tượng begin() và end() giống như cặp đóng mở ngoặc nhọn {...} khi mình lập trình vậy, tất cả mọi họa động vẽ cần được đặt trong cặp begin() - end(), nếu không sẽ báo lỗi. Lát nữa chúng ta sẽ demo sau.

b, Texture:


The Texture class decodes an image file and loads it into GPU memory. The image file should be placed in the "assets" folder. The image's dimensions should be powers of two (16x16, 64x256, etc) for compatibility and performance reasons.
Texture thì có chức năng load hình ảnh vào bộ nhớ GPU, và các hình ảnh này sẽ được đặt trong folder "assets".
Lưu ý: các phiên bản cũ của libGDX yêu cầu Texture phải có kích thước là lũy thừa của 2, nhưng phiên bản hiện tại thì không bị chi phối bởi điều này, tuy nhiên mình vẫn khuyên các bạn nên sử dụng các ảnh có kích thước là lũy thừa của 2 để tăng hiệu năng của game lên.

c, TextureRegion:


Hiểu đơn giản TextureRegion là một bức ảnh nhỏ bạn cắt ra từ một bức ảnh lớn (chính là Texture).

d, Sprite:


Ảnh từ Texture sau khi được load lên ta có thể đưa vào Sprite để sử dụng một số thuộc tính đặc trưng của nó như: position (tọa độ), rotate (xoay), vv,...

Bây giờ, để hiểu rõ hơn chúng ta sẽ đi sang phần demo vẽ hình sử dụng các đối tượng vừa nên trên.

II, Cách vẽ hình trong game:


Đầu tiên đễ vẽ hình chúng ta cần có hình đã đúng không :)).
Mình kết chú chó nâu nên sẽ lấy hình nó vẽ, các bạn có thể lấy hình bất kỳ.



Lấy luôn project hôm trước các bạn tạo ra làm luôn nhé, đầu tiên thì các bạn đưa file hình ảnh vào đúng thư mục "assets" đã nhé.


Các bạn chỉ cần để vào thư mục assets của project android thì tự động bên project desktop nó cũng sẽ được load vào.

Bây giờ các bạn mở project core ra sau đó mở file có trong project ra:

Các bạn xem qua code trong đó:


package com.gameiter.hoclibgdx;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class LearningliGDXb extends ApplicationAdapter {
 SpriteBatch batch;
 Texture img;
 
 @Override
 public void create () {
  batch = new SpriteBatch();
  img = new Texture("badlogic.jpg");
 }

 @Override
 public void render () {
  Gdx.gl.glClearColor(1, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  batch.draw(img, 0, 0);
  batch.end();
 }
}

Bây giờ để vẽ hình chú chó nâu thì bạn chỉ cần đổi tên file được truyền vào hàm khởi tạo Texture là được.
Giải thích qua một chú:
Gdx.gl.glClearColor(1, 0, 0, 1) - dòng code này sẽ set màu mà nền background khi ta chạy game, với ba tham số đầu tương ứng trong gam màu RGB, ở đây (1, 0, 0) có nghĩa là nền màu đỏ, còn ví dụ như (0, 1, 0) thì nên sẽ là màu lục, các bạn có thể tùy chỉnh tham số trong khoảng từ 0 - 1. Còn cái tham số cuối mình cũng chưa tìm hiểu :)) các bạn biết thì comment lại nhé ;), nhưng chủ yếu ta chỉ cần xài 3 tham số đầu, tham số cuối cứ để là 1.

Lưu ý: khác với hệ tọa độ ta học trong series lập trình game java cơ bản, hệ tọa độ trong libGDX giống hệt hệ tọa độ ta học hồi phổ thông, có nghĩa là trục tung hướng lên trên. các bạn xem kết quả sẽ rõ hơn.

Như ở trên chúng ta đã thực hiện việc vẽ hình thông qua đối tượng Texture luôn.
Bây giờ chúng ta sẽ thêm một đối tượng TextureRegion vào để hiểu hơn về nó.

package com.gameiter.hoclibgdx;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class LearningliGDXb extends ApplicationAdapter {
 SpriteBatch batch;
 Texture img;
 TextureRegion region;
 
 @Override
 public void create () {
  batch = new SpriteBatch();
  img = new Texture("chonau.jpg");
  region = new TextureRegion(img, 150, 150);
 }

 @Override
 public void render () {
  Gdx.gl.glClearColor(1, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  batch.draw(img, 0, 0);
  batch.draw(region, 300, 300);
  batch.end();
 }
}

Như các bạn thấy, mình đã thêm vào một đối tượng TextureRegion.
Trong phương thức create() mình khởi tạo đối tượng TextureRegion với ba tham số:

  • Tham số thứ nhất: đối tượng Texture để load ảnh vào GPU.
  • Tham số thứ hai và ba: là kích thức mình muốn cắt ra từ tâm ảnh nhỏ, tọa độ gốc (0, 0) nằm ở góc phía trên bên trái của tấm ảnh góc.
Bây giờ chạy lên chúng ta sẽ có như sau:
Bức ảnh nhỏ sẽ có kích thức 150x150 px như khi mình code.
TextureRegion sẽ được sử dụng nhiều trong việc tạo hoạt ảnh (animation), các bài sau mình sẽ hướng dẫn cụ thể về vấn đề này.

Giờ ta sẽ thử vẽ hình bằng đối tượng Sprite.

Các bạn xem đoạn code dưới đây:

package com.gameiter.hoclibgdx;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class LearningliGDXb extends ApplicationAdapter {
 SpriteBatch batch;
 Texture img;
 TextureRegion region;
 Sprite sprite;
 
 @Override
 public void create () {
  batch = new SpriteBatch();
  img = new Texture("chonau.jpg");
  sprite = new Sprite(img);
 }

 @Override
 public void render () {
  Gdx.gl.glClearColor(1, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  sprite.draw(batch);
  batch.end();
 }
}

Ở trên mình khởi tạo đối tượng Sprite bằng cách truyền vào một Texture.
Trong phương thức render() ta đơn giản là vẽ đối tượng Sprite ra màn hình.

Bây giờ chúng ta sẽ tìm hiểu qua một số phương thức của đối tượng này:

+ Phương thức flip(boolean x, boolean y): phương thức này có chức năng "lật" tấm hình của bạn, tham số thứ nhất đại diện cho chiều ngang (horizontal), tham số thứ hai đại diện cho chiều dọc (vertical). Bạn xem ví dụ sau và thử thay lần lượt các tham số vào sẽ hiểu ngay thôi. Giả sử mình muốn lật ngược tấm hình lại theo chiều dọc mình sẽ thêm vào cuối phương thức create() như sau: sprite.flip(false, true), kết quả như sau:

Nếu bạn muốn lật tấm ảnh theo chiều ngang thì tương tư: sprite.flip(true, fasle), kết qủa:

Đấy, khá thú vị đúng không :))

+ Phương thức rotate(float degrees): phương thức này sẽ set góc quay cho sprite của chúng ta, góc xoay sẽ chạy ngược chiều kim đồng hồ.
Để test, các bạn thêm dòng này vào cuối phương thức create():
sprite.rotate(45); // set góc quay ảnh 45 độ
Kết quả thu được như sau:


+ Phương thức setPosition(float x, float y): sẽ giúp chúng ta set tọa độ cho sprite.
Các bạn có thể thêm dòng này vào cuối phương thức create() để test thử:
sprite.setPosition(200, 150); // đặt tọa độ vẽ sprite (200, 150)
Kết quả như sau:


+ Các bạn cũng có thể tùy chỉnh lại màu của sprite bằng phương thức setColor(Color c).
sprite.setColor(Color.GREEN);
Kết quả:

Và còn một số phương thức khác nữa các bạn có thể tự vọc vạch tìm hiểu thêm nha :D.

Bài hôm nay mình xin kết thúc tại đây, cám ơn các bạn đã ghé thăm blog. Hẹn gặp lại các bạn trong các bài hướng dẫn sau :).


Lập trình game với libGDX - Chạy thử và tìm hiểu cấu trúc Project

Chào mừng các bạn đã quay trở lại với blog :).
Tiếp tục bài hôm trước bây giờ chúng ta sẽ cùng nhau đi tìm hiểu project mà hôm qua chúng ta đã tạo nha.
Và trước khi cùng nhau tìm hiểu thì ta sẽ chạy thử các project xem nó ra ngô ra khoai gì đã ha :D.

Bài hôm nay sẽ gồm một số ý:
  • Imrport project vào eclipse và chạy thử.
  • Tìm hiểu cấu trúc của các project
Ok, chúng ta bắt đầu nhé.



I, Import project vào eclipse và chạy thử:
1, Import project vào eclipse:

Để import các project này vào eclipse các bạn làm như sau:

+ Đầu tiê, bạn vào File/Import, hoặc tại cửa số Package Explorer  bấm chuột phải chọ Import, sau khi chọn thì cửa sổ Import hiện lên như sau:

 Các bạn chọn Existing Project into Workspace, cửa số tiếp theo hiện lên như sau:


Các bạn tiếp tục chọn Browse..., sau khi chọn thì bạn tìm đến thư mục mà các bạn đã tạo các Project thì sẽ được kết quả như sau:

Ví dụ của mình lưu ở đường dẫn: /mnt/Learn/libgdx-1.6.1/Project/LearnlibGDX.
Các bạn thấy sẽ có tất cả 3 project bao gồm:

  • Project android
  • Project core
  • Project desktop
Lưu ý: nếu lúc lựa chọn khi tạo Project bạn chọn iOS hay html thì nó sẽ có thêm các project này vào cho các bạn, bạn nào lỡ tạo mà không muốn import vào thì bỏ tick nha và lưu ý quan trọng là bạn phải import project core vào, vì mình sẽ viết code ở project này.
Bây giờ chúng ta chưa vôi quan tâm đến nó, chạy thử xem như nào đã ha :))

2, Chạy thử các project:

Để chạy thử thì ta sẽ Bấm chuột phải vào project cần chạy (ở đây có 2 project để chạy là android và desktop)

+ Để chạy project dành cho desktop bạn bấm chuột phải vào project desktop -> Run As -> Java Application, sẽ xuất hiện của sổ hỏi bạn chọn file main để thực thi:

Các bạn chọn DesktopLauncher rồi bấm ok. Kết quả thu được như sau:

Rất đơn giản đúng không các bạn :D.

Vậy bây giờ làm sao để chạy project android? Để chạy được project android bạn cần cài đặt một máy ảo (Bạn có thể dùng AVD hoặc Genymotion cũng được), hoặc nếu bạn có máy thật cắm vào chạy thì càng tốt.
À mà mình chắc là giờ project android cảu bạn đang báo lỗi đúng không?
Lỗi này là do ta chưa chọn phiên bản android để build. Để khắc phục các bạn làm như sau:
+ Chuột phải vào project android -> Properties, sẽ xuất hiện của sổ như sau:

+ Các bạn chọn mục Android và chọn một trong các phiên bản ở Project Build Target và bấm OK để hoàn tất.

Xong, giờ muốn chạy thì bạn làm tương tự như chạy project desktop:
+ Bấm chuột phải vào project android -> Run As -> Android Application, Android Emulator mặc định sẽ được bật lên.
+ Nếu báo lỗi thì bạn có thể Run Configurations -> Target, sau đó chọn như hình
Bây giờ bạn có thể chạy bằng máy ảo Genymotion hoặc chọn thiết bị thật đã được kết nối qua USB.

Hiện tại chúng ta chỉ cần test với project destop đã, cái android sau này làm game hoàn chỉnh ta sẽ tính sau ha.

II, Tìm hiểu cấu trúc project:

Thì như các bạn đã thấy, chúng ta có 3 project:
Bây chúng chúng ta sẽ tìm hiểu nha:
* Project core: đây là project chính, game của chúng ta hầu hết sẽ được code ở project này bằng java, các project còn lại khi chạy sẽ lấy code ở core, và đây là cái hay của libGDX: các thư viện trong các project android. desktop sẽ "dịch" code từ core ra để chạy. Bạn xem thử code file chứa trong project desktop xem như nào nhé:

package com.gameiter.hoclibgdx;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class LearningliGDX extends ApplicationAdapter {
 SpriteBatch batch;
 Texture img;
 
 @Override
 public void create () {
  batch = new SpriteBatch();
  img = new Texture("badlogic.jpg");
 }

 @Override
 public void render () {
  Gdx.gl.glClearColor(1, 0, 0, 1);
  Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  batch.begin();
  batch.draw(img, 0, 0);
  batch.end();
 }
}

Ở các phiên bản libGDX cũ thì nó class LearninglibGDX sẽ được implements ApplicationListener, đây là một interface quan trọng, nó được coi là gốc của tất cả các game mà ta viết, nó thể Life cycle của chúng ta.
Thực ra class ApplicationAdapter nó cũng implements thằng ApplicationListener nên bạn viết kiểu nào cũng được.

Chúng ta cùng xem qua interface ApplicationListener xem nó có những gì nhé:


public abstract class ApplicationAdapter implements ApplicationListener {
 @Override
 public void create () {
 }

 @Override
 public void resize (int width, int height) {
 }

 @Override
 public void render () {
 }

 @Override
 public void pause () {
 }

 @Override
 public void resume () {
 }

 @Override
 public void dispose () {
 }
}

Nói sơ qua về các phương thức:
+ create(): sẽ được gọi khi game của chúng ta bắt đầu.
+ resize(int width, int height): phương thức được gọi khi chúng ta thay đổi kích thức màn hình (phóng to, thu nhỏ, vv).
+ render(): đơn giản là vẽ.
+ pasuse(): gọi khi game tạm dừng.
+ resume(): gọi khi game tiếp tục (Sau khi pause() được gọi).
+ dispose(): giúp giải phóng tài nguyên, gọi khi game kết thúc,

Dưới đây là Life cycle (hiểu đơn giản là vòng lặp như mình đã nói ở series lập trình game java cơ bản trước), ảnh mình lấy từ wiki trên github nhé:


Nhìn qua chúng ta cũng phần nào hiểu đước cách thức game vận hành ha :D.

* Còn hai cái project android với desktop kia, có gì trong đó?
Chúng ta cùng xem project desktop nhé, trong này sẽ có file DesktopLauncher, file này đơn giản chỉ là cấu hình để chạy trên desktop các bạn ạ.

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.gameiter.hoclibgdx.LearningliGDXb;

public class DesktopLauncher {
 public static void main (String[] arg) {
  LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
  new LwjglApplication(new LearningliGDXb(), config);
 }
}

Nó chỉ cần vậy là chạy được rồi, vì mọi xử lý đã nằm ở lớp core, việc còn lại của các project kia đơn giản chỉ là cấu hình để chạy.

Mẹo: vì libGDX là open source nên các bạn thoải mái xem code các class nếu tò mò xem nó có gì, để làm điều này trên eclipse nhanh chóng, các bạn giữ Ctrl + chuột trái vào class cần xem nhé.

Còn đây là file AndroidLauncher trong project android, nó cũng chỉ là vài dòng code cấu hình thôi:

import android.os.Bundle;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.gameiter.hoclibgdx.LearningliGDXb;

public class AndroidLauncher extends AndroidApplication {
 @Override
 protected void onCreate (Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
  initialize(new LearningliGDXb(), config);
 }
}

À ngoài ra có một điều khá hay trong libGDX, đó là tất cả tài nguồn có trong game (ví dụ là âm thanh, hình ảnh, vv, ...) ta sẽ lưu vào thư mục assets, và thú vị ở chỗ là ta chỉ cần lưu vào thư mục project assets của android thì lập tức ở thư mục assets của desktop cũng sẽ tự động được cập nhật theo.

Các bạn xem hình sau sẽ rõ hơn (hình mình lấy trong Cuốn Learning LibGDX Game Development, 2nd Edition, các bạn có thể vào mục Sách hay của blog để down bản pdf nhé).


Bài hôm nay vậy mình thấy viết khá nhiều rồi, chúng cũng đã hiểu được phần nào cấu trúc project và life cycle rồi.

Bài hôm sau mình sẽ hướng dẫn các bạn vẽ hình nha. Hẹn gặp lại, các ơm các bạn đã quan tâm đọc bài :D.