ブロック崩し
最近、Javaを勉強しています。
こんな感じのブロック崩しを作りました。
想像以上に難しかったです。
オブジェクト指向って修正がしやすく、わかりやすく書ける気がします。
オブジェクト
- ボール
- 反射板(操作可能)
- 障害物
- 壁
このうちボール以外は線分の組み合わせだと考えられます。
障害物がこのように長方形の場合、4つの線分で障害物を構成できます。
そこで、”線分クラス” を作り、障害物や壁・反射板は ”線分インスタンス”の集まりとしました。
こう考えることで、線分とボールの反射だけを考えるのみで、全ての "オブジェクト-ボールの反射" を考えられます。
どういうことかというと
このようにボールが障害物に向かって進んでいる状況の場合、反射後の位置の計算が必要になります。
これを、”線分インスタンス”とボールの反射が起きているのみだと考えることで
- 記述量が少なくなる(障害物・壁・反射板全て同じ計算になる)
- ボールの軌道も"線分"と考えることで、線分同士の計算となる。よって反射先計算が線分クラス内でまとまる
非常に有益であるといえます。
反射計算
- 衝突するか判定
- 反射後の地点計算
この二つになります。
この状況の場合、
- ボールの線分(軌道)と障害物の線分全てとの交点を計算
- 交点が線分内か検証(線分が交わっているか調べる)
- ボールの線分の支点から、交点への距離を計算
- 一番近い交点で反射が起こる
- 反射先を計算
- 1に戻る
という計算の流れになります。
一番近いというのは、次のような状況への対処が必要なためです。
この場合、交点は4つありますが、反射するのは一番近い交点となります。
よって線分クラスに必要な機能が二つあります。
- 線分同士の交点計算
- 交点が線分内に存在するか(線分が交わっているか)
- 交点から線分の始点(ボールの軌道線分はベクトルとして考える)への距離の計算
交点計算
p_x = -\frac{(x_1'-x_2')(x_1 y_2-x_2 y_1) - (x_1-x_2)(x_1' y_2'-x_2' y_1') }
{(x_1'-x_2')(y_1 - y_2) - (x_1-x_2)(y_1' - y_2')}\\
p_y = \frac{ (y_1-y_2)(x_1' y_2'-x_2' y_1') - (y_1'-y_2')(x_1 y_2-x_2 y_1) }
{(x_1'-x_2')(y_1 - y_2) - (x_1-x_2)(y_1' - y_2')}\\
となります。
ここで、分母が0となるのは、二つの線分が平行であるときです(外積と同じ計算してるので0=平行)。
反射先計算
このように、線分とベクトルがぶつかった状況を考えます。
ぶつかった場合、反射します。
こんな感じです。
次のように名前を付けます。$\vec{e},\vec{e}_v$は線分の単位ベクトルと単位法線ベクトルです。
$\vec{v_2}$を求めることが目標となります。
交点が計算できているため$\vec{v_0},\vec{v_1}$は自明です。$\vec{e},\vec{e}_v$も正規化+90度回転で計算できます。
\vec{d} = - \vec{e}_v (\vec{v_1}*\vec{e}_v)\\
\vec{f} =\vec{e} (\vec{v_1}*\vec{e})\\
\begin{eqnarray}
\therefore \vec{v_2} &=& \vec{f} +\vec{d} \\
&=&\vec{e} (\vec{v_1}*\vec{e}) - \vec{e}_v (\vec{v_1}*\vec{e}_v)
\end{eqnarray}
で計算できます。$*$は内積計算
これで軌道計算が完了です。
プログラミング
Swingを使います。
以前に書いた記事です。ご参考までに
フレームの作成
JFrameを継承したクラスのインスタンスを生成した段階でフレームが生まれます。
BlockBreak frame = new BlockBreak();
タイマー処理
動的に動かすには、一定時間ごとにボールオブジェクトを動かし、反射を調べて、描画を更新していく、必要があります。
初期設定として、
- まずタイマーオブジェクトを生成します。
- タイマーの時間を設定します。
Timer time = new Timer();
time.scheduleAtFixedRate(new SampleTask(), delay, period);
実行内容
public class Timer_task extends TimerTask {
public void run() {
}
}
run()内の処理が一定時間ごとに実行されます。
swingではrepaint()を実行することで描画を更新できます。
repaint()の処理内にはpaint()処理が入っています。
一般にpaint()処理をorverrideして好きな内容に書き換えるみたいです。
以上で説明が必要な部分は終わりです。
後はコードを乗せます。
ほんとはGitとか使った方がいいかもですが、それはまた今度勉強しようと思います。
コード
オブジェクト
BlockBreak extend JFrame
- mainを持つ
- フレーム本体
Ball
- ボール
Manipulate_obj
- 操作板
Obstacle_polygon
- 障害物
Line
- 線分
以上の構成になっています。
本体
BlockBreakクラス
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Timer;
public class BlockBreak extends JFrame implements KeyListener {
private final int height = 600;
private final int width = 600;
private long startTime;
Wall_Line wall;
Ball ball;
Obstacle_polygon obstacle1;
Manipulate_obj board;
ArrayList<Obstacle_polygon> obstacle_list = new ArrayList<>();
public static void main(String[] args){
System.out.println("Start");
BlockBreak frame = new BlockBreak();
java.util.Timer timer = new Timer();
Timer_task timer_task = new Timer_task();
timer_task.setFlame(frame);
timer_task.setTimer(timer);
timer_task.clearEndTimer();
timer.scheduleAtFixedRate(timer_task, 0,30);
}
public BlockBreak() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(this.width, this.height);
setVisible(true);
this.startTime = System.currentTimeMillis();
this.ball = new Ball(200,200,0.5,0.4,Color.red);
ball.set_radius(4);
ball.set_radius_Afterimage(2);
this.wall = new Wall_Line(this.height,this.width,Color.blue);
this.wall.set_wallSize(new int[]{10, 500}, new int[]{10, 500});
this.obstacle1 = new Obstacle_polygon(
new int[]{25, 145, 25, 145},new int[]{25, 25, 145, 145},Color.green);
this.board = new Manipulate_obj(new int[]{200, 350, 350, 200},
new int[]{400, 400, 420, 420},
Color.green);
this.board.set_speed(1.0);
for(int i=0;i<10;i++){
for(int j=0;j<4;j++){
//i 横、j 縦
int x1 = 70+i*40;
int x2 = 70+i*40 + 20;
int y1 = 50 + j*40;
int y2 = 50 + j*40 + 20;
this.obstacle_list.add(new Obstacle_polygon(
new int[]{x1, x2, x2, x1},
new int[]{y1, y1, y2, y2},
Color.green));
}
}
addKeyListener(this);
}
@Override
public void paint(Graphics g){
Image imgBuf = createImage(this.width,this.height);
Graphics gBuf = imgBuf.getGraphics(); //gBufがバッファの画像
gBuf.setColor(Color.white);
gBuf.fillRect(0,0,this.width,this.height);
long now_time = System.currentTimeMillis();
//ボールを動かす
this.ball.move(this.width,this.height);
//Keyboardでの入力でボードを移動する。
//移動距離は時間を測る。
this.board.move_speed(now_time,this.width);
//反射処理
this.reflection();
if(this.wall.ball_outside_judge(this.ball)){
System.out.println("ball outside");
System.out.println(ball.ball_position_toString());
while(true){
continue;
}
}
//位置を更新する
this.ball.update();
//描画処理開始
this.ball.draw(gBuf);
this.wall.draw(gBuf);
//this.obstacle1.draw(gBuf);
this.board.draw(gBuf);
for(Obstacle_polygon poly:this.obstacle_list){
poly.draw(gBuf);
}
//this.ball.draw_velocity(gBuf,height,width);
//バッファを画面に表示
Graphics graphics = getContentPane().getGraphics();
graphics.drawImage(imgBuf,0,0,this);
}
public boolean ball_outside_judge(Ball ball){
//ball が領域外にあるか判定する true->外
double[] position = ball.get_position();
double x = position[0];
double y = position[1];
if(x<0 || width < x ||
y<0 || height < y){
return true;
}else{
return false;
}
}
public void reflection(){
//反射処理:
boolean flag = true;
Line before_reflect_line = null;
Obstacle_polygon min_obj = null;
do {
double min_distance = 1000;
Line min_line = null;
for (Line line : this.wall.line) {
double distance = line.inter_section_distance(ball.ball_line);
if (distance < min_distance && distance != 0
&&!line.same_line(before_reflect_line)) {
min_line = line;
min_distance = distance;
min_obj = null;
}
}
/*
for (Line line : this.obstacle1.line) {
double distance = line.inter_section_distance(ball.ball_line);
if (distance < min_distance && distance != 0
&&!line.same_line(before_reflect_line)) {
min_line = line;
min_distance = distance;
}
}
*/
for (Line line : this.board.line) {
double distance = line.inter_section_distance(ball.ball_line);
if (distance < min_distance && distance != 0
&&!line.same_line(before_reflect_line)) {
min_line = line;
min_distance = distance;
}
}
for(int i=0;i<this.obstacle_list.size();i++){
Obstacle_polygon poly = this.obstacle_list.get(i);
for (Line line : poly.line) {
double distance = line.inter_section_distance(ball.ball_line);
if (distance < min_distance && distance != 0
&&!line.same_line(before_reflect_line)) {
min_line = line;
min_distance = distance;
min_obj = poly;
}
}
}
//min_line と 反射させて ballを更新する
if (min_line != null) {
min_line.reflect(ball);
before_reflect_line = min_line;
if(min_obj != null) {
this.obstacle_list.remove(this.obstacle_list.indexOf(min_obj));
}
}else{
flag = false;
}
//System.out.println(ball.ball_velocity_toString());
//flag = false;
}while(flag);
}
@Override
public void keyTyped(KeyEvent e) {
//使用しないので空にしておきます。
}
@Override
public void keyPressed(KeyEvent e) {
switch ( e.getKeyCode() ) {
case KeyEvent.VK_RIGHT:
if(!this.board.get_state()){
this.board.set_start_time(System.currentTimeMillis());
}
this.board.set_right();
this.board.set_move();
System.out.println("right");
break;
case KeyEvent.VK_LEFT:
if(!this.board.get_state()){
this.board.set_start_time(System.currentTimeMillis());
}
this.board.set_left();
this.board.set_move();
System.out.println("left");
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
switch ( e.getKeyCode() ) {
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_LEFT:
this.board.clear_move();
System.out.println("stop");
break;
}
}
}
TimerTaskクラス
import java.util.Timer;
import java.util.TimerTask;
public class Timer_task extends TimerTask {
BlockBreak frame_timer;
private int num = 0;
private boolean endFlag = true;
Timer timer;
public void run() {
frame_timer.repaint();
num++;
if(num>1000 && endFlag == true){
System.out.println("End");
cancel();
frame_timer.dispose();
timer.cancel();
}
}
public void setFlame(BlockBreak frame){
frame_timer = frame;
}
public void setTimer(Timer time3){
this.timer = time3;
}
public void setEndTimer(){
this.endFlag = true;
}
public void clearEndTimer(){
this.endFlag = false;
}
}
import java.awt.*;
import java.util.ArrayDeque;
import java.util.Queue;
public class Ball {
private double x = 0;
private double y = 0;
private double next_x;
private double next_y;
private double velocity_x = 0;
private double velocity_y = 0;
private Color color;
private Color color_Afterimage;
private long before_time;
private long now_time;
private double radius = 10;
private double radius_Afterimage = 3;
Queue<Double> que_pos_x;
Queue<Double> que_pos_y;
public Line ball_line;
public Ball(int x,int y,double velocity_x,double velocity_y,Color color){
before_time = System.currentTimeMillis();
now_time = System.currentTimeMillis();
this.color = color;
this.color_Afterimage = Color.CYAN;
this.x = x;
this.y = y;
this.velocity_x = velocity_x;
this.velocity_y = velocity_y;
que_pos_x = new ArrayDeque<Double>();
que_pos_y = new ArrayDeque<Double>();
this.ball_line = new Line(x,y,x,y);
}
public void draw(Graphics g){
if(que_pos_x.size()>= 1) {
for (int i = 0; i < que_pos_x.size() ; i++) {
double x = que_pos_x.remove();
double y = que_pos_y.remove();
g.setColor(color_Afterimage);
g.fillOval((int)(x - radius_Afterimage), (int)(y - radius_Afterimage),
(int)(radius_Afterimage * 2), (int)(radius_Afterimage * 2));
que_pos_x.add(x);
que_pos_y.add(y);
}
}
g.setColor(color);
g.fillOval((int)(x - radius), (int)(y - radius), (int)(radius * 2), (int)(radius * 2));
}
public void draw_velocity(Graphics g,double height,double width){
g.setColor(color);
g.drawLine((int)width/2,(int)height/2,
(int)(width/2+this.velocity_x*10),(int)(height/2+this.velocity_y*10));
double x = width/2;
double y = height/2;
double radius = 3;
g.fillOval((int)(x - radius), (int)(y - radius),
(int)(radius * 2), (int)(radius * 2));
}
public void set_radius(int radius){
this.radius = radius;
}
public void set_radius_Afterimage(int radius){
this.radius_Afterimage = radius;
}
public void move(int width,int height){
//次の場所を計算する
this.now_time = System.currentTimeMillis();
long elapsed_time = this.now_time - this.before_time;
this.next_x = this.x + this.velocity_x*elapsed_time;
this.next_y = this.y + this.velocity_y*elapsed_time;
if(this.next_x > width){
this.next_x = width;
}else if(this.next_x <0){
this.next_x = 0;
}
if(this.next_y > height){
this.next_y = height;
}else if(this.next_y <0){
this.next_y = 0;
}
this.before_time = this.now_time;
this.ball_line.set(this.x,this.y,this.next_x,this.next_y);
}
public double[] get_position(){
//x,y,next_x,next_yの順に返す
return new double[]{x,y,next_x,next_y};
}
public double[] get_velocity(){
return new double[]{this.velocity_x,this.velocity_y};
}
public String ball_position_toString(){
String text;
text = "(ball now "
+String.valueOf(this.x)
+" , "
+String.valueOf(this.y)
+")";
return text;
}
public String ball_velocity_toString(){
String text;
text = "("
+String.valueOf(this.velocity_x)
+" , "
+String.valueOf(this.velocity_y)
+")";
return text;
}
public String ball_nposition_toString(){
String text;
text = "(next ball "
+String.valueOf(this.next_x)
+" , "
+String.valueOf(this.next_y)
+")";
return text;
}
public void reflect_UPDOWN(){
this.velocity_y = -1 * this.velocity_y;
}
public void reflect_RIGHTLEFT(){
this.velocity_x = -1 * this.velocity_x;
}
public void set_position(double next_x,double next_y){
//nextの位置を変更する
this.next_x = next_x;
this.next_y = next_y;
}
public void set_velocity(double velocity_x,double velocity_y){
this.velocity_x = velocity_x;
this.velocity_y = velocity_y;
}
public void update(){
que_pos_x.add(this.x);
que_pos_y.add(this.y);
//次の位置に更新する
x = next_x;
y = next_y;
if(que_pos_x.size()>10){
que_pos_x.remove();
que_pos_y.remove();
}
}
}
Lineクラス
public class Line {
private double[][] points = new double[2][2];
public double x_inter;
public double y_inter;
public Line e_vec;
public Line e_vertical_vec;
public Line(double x1,double y1,double x2,double y2){
this.points[0][0] = x1;
this.points[1][0] = x2;
this.points[0][1] = y1;
this.points[1][1] = y2;
}
public void reflect(Ball ball){
//事前に計算された交点ー>反射先終点 にball_lineを更新
//速度方向を変換
this.e_vec(); //単位ベクトル計算
Line v1;
v1 = new Line(0,0,
ball.ball_line.points[1][0] - x_inter,
ball.ball_line.points[1][1] - y_inter);
Line d;
//d = -e2(e2'v1);
this.e_vec();
d = this.e_vertical_vec.product(-1*this.e_vertical_vec.dot(v1));
Line v2;
//v2 = d + e(e'v1)
v2 = d.add_vec(this.e_vec.product(this.e_vec.dot(v1)));
//ball オブジェクトの更新
//ball_line,next,x,y,velocity を 変える
ball.set_position(this.x_inter + v2.points[1][0]
,this.y_inter + v2.points[1][1]);
ball.ball_line.set(this.x_inter,this.y_inter,
this.x_inter + v2.points[1][0],this.y_inter + v2.points[1][1]);
double[] velocity = ball.get_velocity();
double norm_vel = Math.sqrt((velocity[0]*velocity[0] + velocity[1]*velocity[1]));
v2.e_vec();
ball.set_velocity(v2.e_vec.points[1][0]*norm_vel,
v2.e_vec.points[1][1]*norm_vel);
//System.out.println(norm_vel);
//System.out.println("---");
//System.out.println(ball.ball_velocity_toString());
///System.out.println(ball.ball_line.toString());
}
public void set(double x1,double y1,double x2,double y2){
this.points[0][0] = x1;
this.points[1][0] = x2;
this.points[0][1] = y1;
this.points[1][1] = y2;
}
public boolean exist_point(double x1,double y1){
/*
System.out.print("exist point ");
System.out.print(this.points[0][0]);
System.out.print(" ");
System.out.print(this.points[1][0]);
System.out.print(" ");
System.out.println(x1);
*/
//計算誤差で含まれない結果になることを防止するため
//範囲を少し広げるための変数
double error = 1e-10;
if(this.points[0][0] < this.points[1][0] ){
if(this.points[0][0]-error <= x1 &&
x1 <= this.points[1][0] +error){
if(this.points[0][1] < this.points[1][1] ){
if(this.points[0][1] -error <= y1 &&
y1 <= this.points[1][1] +error){
return true;
}
}else{
if(this.points[1][1]-error <= y1 &&
y1 <= this.points[0][1] +error ){
return true;
}
}
}
}else{
if(this.points[1][0]-error <= x1 &&
x1 <= this.points[0][0] +error){
if(this.points[0][1] < this.points[1][1] ){
if(this.points[0][1] -error <= y1 &&
y1 <= this.points[1][1] +error){
return true;
}
}else{
if(this.points[1][1] -error <= y1 &&
y1 <= this.points[0][1] +error){
return true;
}
}
}
}
return false;
}
public void e_vec(){
double x1 = this.points[0][0];
double y1 = this.points[0][1];
double x2 = this.points[1][0];
double y2 = this.points[1][1];
double norm = Math.sqrt((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2));
this.e_vec = new Line(0,0,(x2-x1)/norm, (y2-y1)/norm);
this.e_vertical_vec = new Line(0,0,-1*(y2-y1)/norm,(x2-x1)/norm);
}
public Line add_vec(Line line){
Line line2;
line2 = new Line(this.points[0][0],this.points[0][1],
this.points[1][0] + line.points[1][0],
this.points[1][1] + line.points[1][1]);
return line2;
}
public Line product(double norm){
Line line2;
line2 = new Line(this.points[0][0]*norm,
this.points[0][1]*norm,
this.points[1][0]*norm,
this.points[1][1]*norm);
return line2;
}
public double dot(Line line){
double x1 = this.points[1][0] - this.points[0][0];
double y1 = this.points[1][1] - this.points[0][1];
double x2 = line.points[1][0] - line.points[0][0];
double y2 = line.points[1][1] - line.points[0][1];
double dot;
dot = (x1*x2 + y1*y2);
return dot;
}
public double inter_section_distance(Line ball_line){
//交点を計算ー>交点が両方のラインに含まれるか確認ー>ボールの始点からの距離を返す
double x1 = this.points[0][0];
double y1 = this.points[0][1];
double x2 = this.points[1][0];
double y2 = this.points[1][1];
double x1q = ball_line.points[0][0];
double y1q = ball_line.points[0][1];
double x2q = ball_line.points[1][0];
double y2q = ball_line.points[1][1];
double deno;
deno = (x1q - x2q)*(y1-y2) - (y1q - y2q)*(x1-x2);
//System.out.println(deno);
if(deno < 0.0000001 && deno > -0.0000001){ //平行
return 10000;
}else{
this.x_inter = -1 * ((x1q - x2q) * (x1 * y2 - x2 * y1) - (x1 - x2) * (x1q * y2q - x2q * y1q));
this.x_inter = this.x_inter / deno;
this.y_inter = (y1 - y2) * (x1q * y2q - x2q * y1q) - (y1q - y2q) * (x1 * y2 - x2 * y1);
this.y_inter = this.y_inter / deno;
}
/*
System.out.print("inter point ");
System.out.print(this.x_inter);
System.out.print(" ");
System.out.println(this.y_inter);
*/
boolean cross_flag = (this.exist_point(this.x_inter,this.y_inter)) &&
(ball_line.exist_point(this.x_inter,this.y_inter));
if(cross_flag){
double distance;
distance = Math.sqrt((x1q-this.x_inter)*(x1q-this.x_inter)
+ (y1q-this.y_inter)*(y1q-this.y_inter) );
return distance;
}else{
return 10001;
}
}
public boolean same_line(Line line){
if(line == null){
return false;
}else {
if (this.points[0][0] == line.points[0][0]) {
if (this.points[1][0] == line.points[1][0]) {
if (this.points[0][1] == line.points[0][1]) {
if (this.points[1][1] == line.points[1][1]) {
return true;
}
}
}
}
}
return false;
}
public String toString(){
String text;
text = "("
+String.valueOf(this.points[0][0])
+" , "
+String.valueOf(this.points[0][1])
+") ( "
+String.valueOf(this.points[1][0])
+" , "
+String.valueOf(this.points[1][1])
+")";
return text;
}
}
Obstacle_polygonクラス
import java.awt.*;
public class Obstacle_polygon {
protected Line[] line;
private Color color;
protected int[] x_pos; //多角形点座標 x
protected int[] y_pos; //多角形点座標 y
public Obstacle_polygon(int[] x_pos, int[] y_pos, Color color){
this.line = new Line[x_pos.length];
this.x_pos = x_pos;
this.y_pos = y_pos;
for(int i=0;i<this.x_pos.length-1;i++){
this.line[i] = new Line(x_pos[i],y_pos[i],x_pos[i+1],y_pos[i+1]);
}
this.line[x_pos.length-1] = new Line(x_pos[x_pos.length-1],y_pos[x_pos.length-1],
x_pos[0],y_pos[0]);
this.color = color;
}
public void draw(Graphics g){
g.setColor(color);
Polygon polygon = new Polygon(this.x_pos,this.y_pos,this.x_pos.length);
g.fillPolygon(this.x_pos,this.y_pos,this.x_pos.length);
}
public String ToString(){
String text;
text = "("
+String.valueOf(this.x_pos[0])
+" , "
+String.valueOf(this.y_pos[0])
+") ( "
+String.valueOf(this.x_pos[1])
+" , "
+String.valueOf(this.y_pos[1])
+") ( "
+String.valueOf(this.x_pos[2])
+" , "
+String.valueOf(this.y_pos[2])
+") ( "
+String.valueOf(this.x_pos[3])
+" , "
+String.valueOf(this.y_pos[3])
+")";
return text;
}
}
Manipulate_objクラス
import java.awt.*;
public class Manipulate_obj extends Obstacle_polygon{
private double speed = 0.1;
public boolean move_enable = false;
private long start_time = 0;
public Manipulate_obj(int[] x_pos, int[] y_pos, Color color){
super(x_pos,y_pos,color);
this.start_time = System.currentTimeMillis();
}
public void move(int x,int width){
for(int i=0;i<this.x_pos.length;i++){
if(this.x_pos[i]+x < 0){
x = this.x_pos[i]*-1;
} else if (this.x_pos[i] + x > width) {
x = width - this.x_pos[i];
}
}
for(int i=0;i<this.x_pos.length;i++){
this.x_pos[i] = this.x_pos[i] + x;
}
for(int i=0;i<this.x_pos.length-1;i++){
this.line[i] = new Line(x_pos[i],y_pos[i],x_pos[i+1],y_pos[i+1]);
}
this.line[x_pos.length-1] = new Line(x_pos[x_pos.length-1],y_pos[x_pos.length-1],
x_pos[0],y_pos[0]);
}
public void set_speed(double speed){
this.speed = speed;
}
public void set_start_time(long time){
this.start_time = time;
}
public void set_move(){
this.move_enable = true;
}
public void clear_move(){
this.move_enable = false;
}
public boolean get_state(){
if(this.move_enable == true){
return true;
}else{
return false;
}
}
public void set_right(){
this.speed = Math.abs(this.speed);
}
public void set_left(){
this.speed = Math.abs(this.speed)*-1;
}
public void move_speed(long now_time,int width){
if(this.move_enable == true){
double x = speed * (now_time - this.start_time);
this.move((int)x,width);
set_start_time(now_time);
}
}
}