質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

2988閲覧

Java 音楽ゲーム

Puhu

総合スコア31

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

1クリップ

投稿2015/12/02 15:07

現在、javaで音楽ゲームを作成しています。
まずは譜面を降らしてみようと思い、ランダムで生成された譜面が流れるプログラムを作成したのですが、同時押しを描画する際に
譜面のズレが生じます。
repaintの位置を変えたりしてもズレてしまい、自分では解決出来ない状況です。

原因になりそうなものがあればご指摘いただきたいです。
よろしくお願いします。

import java.awt.Color;
import java.awt.Graphics;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class FallingLove {

public static void main(String[] args){ new vimani(); }

}

class vimani extends JPanel{

private JFrame frame; private FallObj[] fobj = new FallObj[5];//4つ落ちるレーンを用意 public vimani(){ window();//ウィンドウを生成 Timer addObj = new Timer(); addObj.schedule(new AddObj(), 0,220);//好きなタイミングでobj落下 long error = 0; int fps = 30; long idealSleep = (1000 << 16) / fps; long oldTime; long newTime = System.currentTimeMillis() << 16; while (true) { oldTime = newTime; for(int i = 0; i < 5 ; i++){//5ノーツ分 fobj[i].fall(i);//fallを呼び出す。 } newTime = System.currentTimeMillis() << 16; long sleepTime = idealSleep - (newTime - oldTime) - error; // 休止できる時間 if (sleepTime < 0x20000){ sleepTime = 0x20000; // 最低でも2msは休止 }else{ try{ oldTime = newTime; Thread.sleep(sleepTime >> 16); // 休止 newTime = System.currentTimeMillis() << 16; error = newTime - oldTime - sleepTime; // 休止時間の誤差 }catch(Exception eee){ System.err.println(eee.getMessage()); } } } } //全てのノーツを同時に同じだけ下降させる class Fall{ public void run(){ for(int i = 0; i < 5 ; i++){//4ノーツ分 fobj[i].fall(i);//fallを呼び出す。 } } } //仮としてノーツをランダム配置する class AddObj extends TimerTask{ Random rand = new Random(); int dCheck = 0; int taihi = 0; public void run(){ taihi = rand.nextInt(5);//ランダムで5レーンから1レーン選択 fobj[taihi].addObj();//そこにノーツ生成 if(dCheck == 0){ dCheck = rand.nextInt(12);//0~11の乱数生成 if(dCheck == 7 || dCheck == 11){//7か11であれば do{ dCheck = rand.nextInt(5);//被らない場所に同じタイミングでもう一つノーツ配置 }while(taihi == dCheck); fobj[dCheck].addObj();//2つ目のノーツ配置 } taihi = 0; dCheck = 0;//初期化。 } } } //譜面を降らすウィンドウ、レーンの作成 private void window(){ frame = new JFrame(); frame.setTitle("譜面フラスマン"); frame.setBounds(600,10,600,1000);//x=600,y=10,横600,縦1000のフレームを準備 frame.setLayout(null);//レイアウトマネージャ使用しない frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//一緒に消す for(int i=0;i<5;i++){ fobj[i] = new FallObj(); fobj[i].setSize(30,600);//レーンは横30、縦600 fobj[i].setLocation(220 + 35 * i,10);//レーンの位置 frame.add(fobj[i]); } frame.setVisible(true); }

}//class vimani close

//JPanelとしてGraphic描画するクラス

class FallObj extends JPanel{
private static final long serialVersionUID = 1L;
private int[] obj = new int[0];
public FallObj(){
setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V
}

/Obj配列を新しく追加/
public void addObj(){
int[] o = new int[obj.length +1];
for(int i = 0; i < obj.length ;i++){
o[i] = obj[i];
}
obj = o;
}

/Obj配列の先頭を削除/
private void delObj(int cnt){
int[] o = new int[obj.length -1];
for(int i = 0; i < o.length ;i++){
o[i] = obj[i +1];
}
obj = o;
}

/呼び出すごとにObj配列の値を+1する/
public void fall(int fallCount){
for(int i = 0; i < obj.length ; i++){
obj[i] += 15;//落下するスピード

if(600 < obj[i]){//消す位置。今はy=600で消してる。 delObj(i);//この位置に来た先頭のObj配列を削除。 } } repaint(); }

/実際に描画するメソッド/
public void paintComponent(Graphics g){
super.paintComponent(g);

g.setColor(new Color(102,255,204,255));//ノーツの色 for(int i : obj){ g.fillRect(0, i, 180, 20);//オブジェクトの位置と大きさ } }

}//class FallObj close

owatanohika👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ご質問のゲーム全体を解析したわけではないですが、解析は少し難しいので、まずは一般論でお答えします。

ゲーム等の画面のリフレッシュは、FPS(Frame Per Second)というもので考えられるのが一般的です。
つまり、一定間隔で画面の再描画を行うのです。

このゲームではfall()メソッドが行われたタイミングでのみ再描画を行っています。

そうではなく、一定の間隔で必ず再描画を行うようにしてみてはどうでしょう。
再描画専用のスレッドを追加して、そのスレッドはスリープと再描画だけを行うようにします。

投稿2015/12/03 07:03

argius

総合スコア9390

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Puhu

2015/12/05 00:16

なるほど...やってみます! ありがとうございました!
Puhu

2015/12/05 07:36

import java.awt.Color; import java.awt.Graphics; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import javax.swing.JFrame; import javax.swing.JPanel; public class FallingLove { public static void main(String[] args){ new vimani(); } } class vimani extends JPanel{ private JFrame frame; public FallObj[] fobj = new FallObj[5];//5つ落ちるレーンを用意 public vimani(){ window();//ウィンドウを生成 //Timer repaint = new Timer(); //repaint.schedule(new Rep(), 0,1); Timer addObj = new Timer(); addObj.schedule(new AddObj(), 0,220); //好きなタイミングでobj落下 Timer t = new Timer(); t.schedule(new Fall(), 0,30); runi(); } //画面更新 //class Rep extends{ public void runi(){ long error = 0; int fps = 60000; long idealSleep = (1000 << 16) / fps; long oldTime; long newTime = System.currentTimeMillis() << 16; while (true) { oldTime = newTime; for(int i = 4; i > -1 ; i--){//4ノーツ分 fobj[i].repai();//repaintを呼び出す。 } newTime = System.currentTimeMillis() << 16; long sleepTime = idealSleep - (newTime - oldTime) - error; // 休止できる時間 if (sleepTime < 0x20000){ sleepTime = 0x20000; // 最低でも2msは休止 }else{ try{ oldTime = newTime; Thread.sleep(sleepTime >> 16); // 休止 newTime = System.currentTimeMillis() << 16; error = newTime - oldTime - sleepTime; // 休止時間の誤差 }catch(Exception eee){ System.err.println(eee.getMessage()); } } } } //} //全てのノーツを同時に同じだけ下降させる class Fall extends TimerTask{ public void run(){ for(int i = 0; i < 5 ; i++){//4ノーツ分 fobj[i].fall(i);//fallを呼び出す。 } } } //仮としてノーツをランダム配置する class AddObj extends TimerTask{ Random rand = new Random(); int dCheck = 0; int taihi = 0; public void run(){ taihi = rand.nextInt(5);//ランダムで5レーンから1レーン選択 fobj[taihi].addObj();//そこにノーツ生成 if(dCheck == 0){ dCheck = rand.nextInt(12);//0~11の乱数生成 if(dCheck == 7 || dCheck == 11){//7か11であれば do{ dCheck = rand.nextInt(5);//被らない場所に同じタイミングでもう一つノーツ配置 }while(taihi == dCheck); fobj[dCheck].addObj();//2つ目のノーツ配置 } taihi = 0; dCheck = 0;//初期化。 } } } //譜面を降らすウィンドウ、レーンの作成 private void window(){ frame = new JFrame(); frame.setTitle("譜面フラスマン"); frame.setBounds(600,10,600,1000);//x=600,y=10,横600,縦1000のフレームを準備 frame.setLayout(null);//レイアウトマネージャ使用しない frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//一緒に消す for(int i=0;i<5;i++){ fobj[i] = new FallObj(); fobj[i].setSize(30,600);//レーンは横30、縦600 fobj[i].setLocation(220 + 35 * i,10);//レーンの位置 frame.add(fobj[i]); } frame.setVisible(true); } }//class vimani close //JPanelとしてGraphic描画するクラス class FallObj extends JPanel{ private static final long serialVersionUID = 1L; private int[] obj = new int[0]; public FallObj(){ setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V } /*Obj配列を新しく追加*/ public void addObj(){ int[] o = new int[obj.length +1]; for(int i = 0; i < obj.length ;i++){ o[i] = obj[i]; } obj = o; } /*Obj配列の先頭を削除*/ private void delObj(int cnt){ int[] o = new int[obj.length -1]; for(int i = 0; i < o.length ;i++){ o[i] = obj[i +1]; } obj = o; } /*呼び出すごとにObj配列の値を+1する*/ public void fall(int fallCount){ for(int i = 0; i < obj.length ; i++){ obj[i] += 15;//落下するスピード if(600 < obj[i]){//消す位置。今はy=600で消してる。 delObj(i);//この位置に来た先頭のObj配列を削除。 } } repaint(); } public void repai(){ repaint(); } /*実際に描画するメソッド*/ public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(new Color(102,255,204,255));//ノーツの色 for(int i : obj){ g.fillRoundRect(0, i, 180, 20,5,10);//オブジェクトの位置と大きさ } } }//class FallObj close プログラムをこのように (vimaniクラスの runi();メソッド) 改変し、画面更新だけのメソッドを動かすようにしましたが、やはりズレが生じてしまいます。 これは画面更新というか、画面描画の方法に問題があるのでしょうか...
argius

2015/12/05 08:09

少しコードを解析してみました。 レーンが複数のパネルに分かれているんですね。 複数のパネルの描画を同期させるのは難しいかも知れません。 fall()の再描画を止めて、JFrame単位で再描画させるようにしてみましたが、それでも変わりませんでした。 できれば、1つのパネルに直接、複数のレーンを描画するようにした方が良いかも知れません。
Puhu

2015/12/05 10:49

すみません、ありがとうございます! そうですか...すこし描きなおしてみます! ありがとうございました!
Puhu

2015/12/06 06:34

一日いろいろやってみましたが、一つのパネル内に複数のオブジェクトを描画する方法が解りませんでした...、 複数lane.setLocation等で設定しても、最後に設定した一つのオブジェクトしか表示させることができません、どのようにすれば実装できますでしょうか。
argius

2015/12/06 06:52

重ね塗りの要領です。 前回書いた図形は消えてしまうので、動かない図形も毎回描画する必要があります。 まずレーンを固定位置で描画して、その上からノーツを描画します。 後から書いた描画が上書きになるので、ノーツは後から書きます。 パネルは1つだけ用意します。 そのパネルのpaintConponentメソッド内で全ての図形を描画します。 そして、そのパネルの再描画を一定間隔でrepaintさせます。
Puhu

2015/12/06 08:53

現在 window()メソッドの中を  private void window(){ frame = new JFrame(); frame.setTitle("譜面フラスマン"); frame.setBounds(600,10,600,1000);//x=600,y=10,横600,縦1000のフレームを準備 frame.setLayout(null);//レイアウトマネージャ使用しない frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//一緒に消す JPanel lane1 = new JPanel(); lane1.setSize(30,600);//レーンは横30、縦600 lane1.setLocation(220,10);//レーンの位置 (220 + 35 * i , 10 ) lane1.setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V lane1.setSize(30,600);//レーンは横30、縦600 lane1.setLocation(255,10);//レーンの位置 (220 + 35 * i , 10 ) lane1.setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V lane1.setSize(30,600);//レーンは横30、縦600 lane1.setLocation(290,10);//レーンの位置 (220 + 35 * i , 10 ) lane1.setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V lane1.setSize(30,600);//レーンは横30、縦600 lane1.setLocation(325,10);//レーンの位置 (220 + 35 * i , 10 ) lane1.setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V lane1.setSize(30,600);//レーンは横30、縦600 lane1.setLocation(360,10);//レーンの位置 (220 + 35 * i , 10 ) lane1.setBackground(new Color(153,51,51,255));//レーンの色。 R,G,B,V frame.add(lane1); frame.setVisible(true); } } このように変更しました、JPanel lane1 = new JPanel(); として一つのパネル内に四角形を描画しようとしたのですが、最後に設定した物しか表示されません。 当然、同じ lane1 に設定しているのですからこうなるのは分っているのですが... 一つのPanel内に複数四角形を配置する別の方法があるのでしょうか...
argius

2015/12/06 09:04

パネルを1つのキャンバスに見立てて、そこに全て描画します。 ノーツの描画と同じように、レーンもdrawRectかfillRectを使って描画すれば良いです。 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // レーン1 g.drawRect(10, 0, 20, 400); // レーン1のノート g.fillRect(10, 10, 20, 20); // レーン2 g.drawRect(110, 0, 20, 400); // レーン2のノート g.fillRect(110, 30, 20, 20); }
Puhu

2015/12/06 09:45

ありがとうございます! また色々試してみようと思います!!
Puhu

2015/12/06 11:44

何度もすみません... 一つJPanelを用意し、それを継承させてPaintComponentを作成し、repaintをさせてみたのですが、画面に何も表示されなくなってしまいました。Panelに何も設定してない(大きさ、位置、色等)がいけないのかと思い、設定させてみましたが、そのPanelすらも表示されていません。 それとも更新が速すぎて見えていないだけとか、そういうものでしょうか。 ************************:: import java.awt.Color; import java.awt.Graphics; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import javax.swing.JFrame; import javax.swing.JPanel; public class nmg { public static void main(String[] args){ new vimani(); } } class vimani extends JPanel{ private JFrame frame; // public int[] lane = new int[5];//5つ落ちるレーンを用意 public lanePanel violin; public vimani(){ window();//ウィンドウを生成 runi(); } public void window(){ frame = new JFrame(); frame.setTitle("譜面フラスマン"); frame.setBounds(600,10,600,1000);//x=600,y=10,横600,縦1000のフレームを準備 frame.setLayout(null);//レイアウトマネージャ使用しない frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//一緒に消す violin = new lanePanel(); violin.setSize(600,1000); violin.setLocation(600,10); violin.setBackground(new Color(0,255,255,255)); frame.add(violin); frame.setVisible(true); } public void runi(){ long error = 0; int fps = 60; long idealSleep = (1000 << 16) / fps; long oldTime; long newTime = System.currentTimeMillis() << 16; while (true) { oldTime = newTime; violin.repaint();//repaintを呼び出す。 newTime = System.currentTimeMillis() << 16; long sleepTime = idealSleep - (newTime - oldTime) - error; // 休止できる時間 if (sleepTime < 0x20000){ sleepTime = 0x20000; // 最低でも2msは休止 }else{ try{ oldTime = newTime; Thread.sleep(sleepTime >> 16); // 休止 newTime = System.currentTimeMillis() << 16; error = newTime - oldTime - sleepTime; // 休止時間の誤差 }catch(Exception eee){ System.err.println(eee.getMessage()); } } } } public class lanePanel extends JPanel{ @Override public void paintComponent(Graphics g) { super.paintComponent(g); // レーン g.setColor(new Color(153,51,51,255)); g.drawRect(10, 0, 20, 400); g.drawRect(110, 0, 20, 400); //ノート g.setColor(new Color(102,255,204,255));//ノーツの色 g.fillRect(10, 10, 20, 20); g.fillRect(110, 30, 20, 20); } } }
argius

2015/12/06 11:53

JPanelの位置は、JFrameからの相対位置なので、 violin.setLocation(600, 10); だとフレームからはみ出しています。
Puhu

2015/12/06 12:33

げ、そうでした...解決しました。 すみません...失礼しました
Puhu

2016/01/03 14:35

ご指摘いただき、レーンを4つに分けて表示するようにしましたが、こうした場合にどのように譜面をどんどんふらせていけばいいのか、またどのように振っている譜面をズレなく更新すればいいのかがわかりません。 ノーツを表示し、降らせることだけは出来るのですが、そこで止まっています。 レーンに随時ノーツが増えていき、ほぼ誤差なく同じだけ毎秒すすませていきたいのですが、このようなことができる処理で似たようなことをしているものなどはありますでしょうか? それとも実は気付いてないだけで簡単にできるよ、ということでしたらヒントを頂きたいです。 以下、現在のソースコードです。 import java.awt.Color; import java.awt.Graphics; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import javax.swing.JFrame; import javax.swing.JPanel; public class nmg2 { public static void main(String[] args){ new vimani(); } } class vimani extends JPanel{ private JFrame frame; public lanePanel violin; static int[][] noteCase = new int[5][256]; public vimani(){ window();//ウィンドウを生成 runi(); } public void window(){ frame = new JFrame(); frame.setTitle("譜面フラスマン"); frame.setBounds(600,10,600,1000);//x=600,y=10,横600,縦1000のフレームを準備 frame.setLayout(null);//レイアウトマネージャ使用しない frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//一緒に消す noteCase[0][0] = 1; noteCase[4][0] = 1; violin = new lanePanel(); violin.setSize(600,1000); violin.setBackground(new Color(255,255,255,255)); frame.add(violin); frame.setVisible(true); } public void runi(){ long error = 0; int fps = 60; long idealSleep = (1000 << 16) / fps; long oldTime; long newTime = System.currentTimeMillis() << 16; while (true) { oldTime = newTime; violin.repaint();//repaintを呼び出す。 newTime = System.currentTimeMillis() << 16; long sleepTime = idealSleep - (newTime - oldTime) - error; // 休止できる時間 if (sleepTime < 0x20000){ sleepTime = 0x20000; // 最低でも2msは休止 }else{ try{ oldTime = newTime; Thread.sleep(sleepTime >> 16); // 休止 newTime = System.currentTimeMillis() << 16; error = newTime - oldTime - sleepTime; // 休止時間の誤差 }catch(Exception eee){ System.err.println(eee.getMessage()); } } } } public class lanePanel extends JPanel{ private int[] obj = new int[0]; protected int minutes=0; @Override public void paintComponent(Graphics g) { super.paintComponent(g); // レーン g.setColor(new Color(153,51,51,255)); g.fillRect(220,10, 30, 600); g.fillRect(255,10, 30, 600); g.fillRect(290,10, 30, 600); g.fillRect(325,10, 30, 600); g.fillRect(360,10, 30, 600); //ノート g.setColor(new Color(102,255,204,255));//ノーツの色 g.fillRoundRect(220, minutes+10, 30, 20, 5, 10); g.fillRoundRect(255, minutes+30, 30, 20, 5, 10); minutes+=5; } } }
argius

2016/01/04 05:35

この手のゲームは、出現するノートは曲ごとにランダムになるわけではないんですよね? それならば、リアルタイムの処理が開始する前にあらかじめ可能な限り計算を済ませておけば、描画時の計算が減ってその分をリアルタイムの処理に回せます。 具体的には、配列にあらかじめノートの相対位置を設定しておき、現在の時間との相対位置でノートの描画位置を決めます。 最初は、全部のノートを描画するところまでプログラミングして、ざっくりと動くところまで作ってしまいましょう。 ラフスケッチみたいなものです。 あとは、描画にかくつきやちらつきが無くなるようにチューニングしていきます。 画面の範囲外になるような部分は描画をスキップするとか。 如何にリアルタイム処理時に状態遷移やpaintComponent内の処理を高速に終わらせることができるかがポイントです。 間引ける処理は間引いていったりします。 これ以上は、私もそれほどゲーム制作に詳しいわけではないので、 ゲームプログラミングテクニックなどの記事や書籍を当たってみたほうが良いと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問