Thứ Bảy, 2 tháng 2, 2013

Di chuyển sprite đi thẳng




Xác định đường đi cho sprite, hay sprite đi thẳng.


Nếu bạn muốn dịch chuyển một sprite từ điểm này đến điểm khác theo một đường thẳng, bạn phải tính toán đường thẳng đó theo thông tin của hai điểm. Đường thẳng là đồ thị của hàm 

mx+b=y

bạn phải tính toán slope - độ dốc (m) và intercep - chặn (b) để có thể tính được mọi vị trí giữa điểm đi và điểm đến. Dưới đây là code làm việc đó trong J2ME:


class Util {
  /**
   * Slope calculation for line given by two x-y-positions
   *
   * @param x1 First position's x-parameter
   * @param y1 First position's y-parameter
   * @param x2 Second position's x-parameter
   * @param y2 Second position's y-parameter
   * @return calculated slope value
  */
  public static double calcLineSlope(int x1, int y1, int x2, int y2) {
    double dx = x1 - x2;
    double dy = y1 - y2;
    if ((dx != 0) && (dy != 0)) {
      return (dy / dx);
    } else {
      return 0;
    }
  }

  /**
   * Intercept calculation for line given by x-y-position and slope
   *
   * @param x x-parameter of given position
   * @param y y-parameter of given position
   * @param m slope of line
   * @return calculated intercept value
   */
  public static double calcLineIntercept(int x, int y, double m) {
    if (m != 0) {
      return y - m*x;
    } else {
      return 0;
    }
  }
}

Để đoạn code trên hoạt động, bạn có thể dùng một class EnemySprite như sau

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

class EnemySprite extends Sprite {

  double cur_m;
  double cur_b;
  int target_x;
  int target_y;

  int speed;

  // Constructor, is called when you instantiate your EnemySprite
  public EnemySprite(Image img) {
    super(img);
    
    speed = 5;

    // Starting position (20, 20), target position (30, 100)
    this.setPosition(20, 20);
    target_x = 30; target_y = 100;
    cur_m = Util.calcLineSlope(target_x, target_y, this.getX(), this.getY());
    cur_b = Util.calcLineIntercept(this.getX(), this.getY(), cur_m);
  }
  
  // Call this method in your game loop
  public void update () {
    if ((this.getX() != target_x) || (this.getY() != target_y)) {
      double dx = Math.abs(target_x - this.getX());
      double dy = Math.abs(target_y - this.getY());
      if ((dx != 0) && (dy != 0)) {
        int nextx = 0;
        
        if (Math.abs(this.getX() - target_x) < speed) {
          nextx = target_x;
        } else {
          nextx = (this.getX() < target_x) ? 
            this.getX() + speed :
            this.getX() - speed;
        }
        int nexty = (int)(nextx * cur_m + cur_b);
      
        this.setPosition(nextx, nexty);
      } else {
        if (dx != 0) {
          if (Math.abs(this.getX() - target_x) < 2*speed) {
            this.setPosition(target_x, this.getY());
          } else {
            int nextx = (this.getX() < target_x) ? 
              this.getX() + 2*speed :
              this.getX() - 2*speed;
            this.setPosition(nextx, this.getY());
          }
        }
        if (dy != 0) {
          if (Math.abs(this.getY() - target_y) < 2*speed) {
            this.setPosition(this.getX(), target_y);
          } else {
            int nexty = (this.getY() < target_y) ? 
                this.getY() + 2*speed:
                this.getY() - 2*speed;
            this.setPosition(this.getX(), nexty);
          }
        }
      }
    } else {

      // Target position reached, inititalize next waypoint...

    }
  }
}


Chú ý, đoạn code trên chỉ hoạt động trên MIDP 2.0 trở lên, vì theo tôi biết, MIDP 1.0 không có giá trị float.

Red_Scorpion dịch từ http://j2me-focus.blogspot.com/2007/06/defining-waypoints-for-your-sprite-or.html

Tạo game full screen


Bạn viết Game thì mặc định sẽ không đầy màn hình ( full screen ) đâu. Hãy xem hình dưới đây

Bạn thấy đấy, game co vào giữa trông rất xấu, trên và dưới khung game có menu và option của điện thoại bạn đang dùng. Vậy làm sao để viết game full screen? Các bạn chỉ cần thêm đoạn code sau ( tô đậm ) vào constructor của lớp mở rộng của lớp GameCanvas


public clsCanvas(midMain m) {
   super(true);
   fParent = m;
   setFullScreenMode(true);
}


Khi bạn dùng phương thức setFullScreenMode( ) nếu truyền tham số true thì game sẽ full screen ( để false thì thà không dùng còn hơn ). Trên đa số điện thoại di động, sau khi viết game các bạn sẽ có kết quả vừa ý, chẳng hạn như sau

Tuy nhiên, với một số điện thoại, chẳng hạn Nokia N70, các bạn cũng gọi phương thức đó, cũng với tham số true, chạy thử trên giả lập của IDE thì ok nhưng khi vào máy thật kết quả sẽ ra thế này

Cái này tôi cũng không biết vì sao. Nhưng Devlin đã có một giải pháp rất đơn giản cho việc này, tôi đã làm thử và thành công. Đó là thay vì bạn gọi phương thức setFullScreenMode(true) trong constructor của lớp mở rộng của GameCanvas thì bạn gọi ở ngoài. Ở đây tôi gọi trong lớp MIDlet sau khi khai báo biến với giá trị là lớp đó ( là lớp clsCanvas ).


public class midMain extends MIDlet {
    private clsCanvas myCanvas;

    public void startApp() {
        myCanvas=new clsCanvas(this);
        myCanvas.setFullScreenMode(true);
        myCanvas.start();
        Display.getDisplay(this).setCurrent(myCanvas);
    }


Và sau đó, kết quả !!!

Chúc các bạn thành công !!

Vài điều muốn nói


Tôi là một người đang tự học J2ME. Cũng như mọi người, tôi lên mạng tìm tài liệu để học theo. Và tôi tìm được trang blog của Devlin, một blog bằng tiếng Anh. Tôi đã bookmark nó một lần, sau đó một thời gian ổ cứng của tôi có vấn đề và mất các bookmark như thế... Tôi nghĩ, tại sao mình không làm một blog, lưu trữ các bài viết giá trị và chia sẻ cho mọi người? Đó là nguyên nhân blog này ra đời. Tôi lượm lặt thông tin, các tutorial, các bài học... và dịch nó sang tiếng Việt để mọi người xem, trình độ tiếng Anh của tôi không cao nên dịch còn lủng củng, mong mọi người thông cảm.

Mong các bạn ủng hộ, có gì thắc mắc hãy liên hệ với tôi qua email scorpion06.vs.knight@gmail.com , tôi sẽ cố gắng giải thích trong tầm hiểu biết của mình, tôi cũng rất muốn được tiếp thu ý kiến của mọi người. Xin cảm ơn !!