チーム1431
課題名
音感試しゲーム,ディスプレイ鍵盤
研究者名
15組1番 青木 駿
15組2番 新井 謙輔
概要
音感試しゲーム
スイッチ基盤のスイッチを押すことによって音の出力を行い,その出力の場合分けに使用した文字をシリアル通信でパソコンに送信した.これに加えてProcessingを使って鍵盤を模した画面を表示し,出力された音の音階に該当する鍵にオブジェクトを移動させるゲームを作成した.
ディスプレイ鍵盤
Processingを使って作成した鍵盤の画面を,今度は本当に鍵盤として機能させた.画面の鍵に乗っているとき,その鍵に該当する音階の音をスピーカーから鳴らすようにした.
使用したもの
・PSoC
・PSoC MiniProg
・スピーカー
・スイッチ基盤
・ジャンパ線
ソースプログラム
・PSocのソースプログラム(音感試しゲーム)
#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
#define A 0b10001000
#define I 0b00100100
#define B 0b10000100
#define J 0b00100010
#define C 0b10000010
#define D 0b10000001
#define K 0b00011000
#define E 0b01001000
#define L 0b00010100
#define F 0b01000100
#define M 0b00010010
#define G 0b01000010
#define N 0b00010001
#define H 0b01000001
int silent=0, d=229, l=204, m=182, f=172, s=153, r=136, c=121, dd=115, da=217, la=193, fa=162, sa=145, ra=129, dda=108;
void wait(){
long i;
for(i=0;i<40000;i++);
}
void main(void)
{
// M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts // Insert your main routine code here. UART_CmdReset(); UART_IntCntl(UART_ENABLE_RX_INT); Counter8_WritePeriod(155); Counter8_WriteCompareValue(77); Counter8_Start(); UART_Start(UART_PARITY_NONE); M8C_EnableGInt; PWM16_1_Start(); UART_CPutString("\r\nInput from external switches. V1.1 \r\n"); LCD_1_Start(); while(1){ PRT1DR=0b10000000; if(PRT1DR==A){ PWM16_1_WritePeriod(d); wait(); LCD_1_PrCString("A"); while(PRT1DR==A) UART_CPutString("A"); } else if(PRT1DR==B){ PWM16_1_WritePeriod(l); wait(); LCD_1_PrCString("B"); while(PRT1DR==B) UART_CPutString("B"); } else if(PRT1DR==C){ PWM16_1_WritePeriod(m); wait(); LCD_1_PrCString("C"); while(PRT1DR==C) UART_CPutString("C"); } else if(PRT1DR==D){ PWM16_1_WritePeriod(f); wait(); LCD_1_PrCString("D"); while(PRT1DR==D) UART_CPutString("D"); }
else PRT1DR=0b00000000; PRT1DR=0b01000000; if(PRT1DR==E){ PWM16_1_WritePeriod(s); wait(); LCD_1_PrCString("E"); while(PRT1DR==E) UART_CPutString("E"); } else if(PRT1DR==F){ PWM16_1_WritePeriod(r); wait(); LCD_1_PrCString("F"); while(PRT1DR==F) UART_CPutString("F"); } else if(PRT1DR==G){ PWM16_1_WritePeriod(c); wait(); LCD_1_PrCString("G"); while(PRT1DR==G) UART_CPutString("G"); } else if(PRT1DR==H){ PWM16_1_WritePeriod(dd); wait(); LCD_1_PrCString("H"); while(PRT1DR==H) UART_CPutString("H"); } else PRT1DR=0b00000000; PRT1DR=0b00100000; if(PRT1DR==I){ PWM16_1_WritePeriod(da); wait(); LCD_1_PrCString("I"); while(PRT1DR==I) UART_CPutString("I"); } else if(PRT1DR==J){ PWM16_1_WritePeriod(la); wait(); LCD_1_PrCString("J"); while(PRT1DR==J) UART_CPutString("J"); } else PRT1DR=0b00000000; PRT1DR=0b00010000; if(PRT1DR==K){ PWM16_1_WritePeriod(fa); wait(); LCD_1_PrCString("K"); while(PRT1DR==K) UART_CPutString("K"); } else if(PRT1DR==L){ PWM16_1_WritePeriod(sa); wait(); LCD_1_PrCString("L"); while(PRT1DR==L) UART_CPutString("L"); } else if(PRT1DR==M){ PWM16_1_WritePeriod(la); wait(); LCD_1_PrCString("M"); while(PRT1DR==M) UART_CPutString("M"); } else if(PRT1DR==N){ PWM16_1_WritePeriod(dda); wait(); LCD_1_PrCString("N"); while(PRT1DR==N) UART_CPutString("N"); } else PRT1DR=0b00000000; // if(PRT1DR==0b00000000) // PWM16_1_WritePeriod(silent); UART_CmdReset(); }
}
・Processingのソースプログラム(音感試しゲーム)
import processing.serial.*;
String psocPort = Serial.list()[Serial.list().length-1];
Serial port=new Serial(this,psocPort,38400);
ArrayList<Block> blocks = new ArrayList<Block>();
Player player;
PVector basePos = new PVector();
int jumpcount=0;
void setup() {
size(800, 600, P3D); background(30); blocks.add(player = new Player(100, 100)); for(int i=0; i<8; i++) { if(i!=2 && i!=6) blocks.add(new Block(i*100+40,80,80,80, color(0))); blocks.add(new Block(i*100,500,80,80,color(240,240,240))); } blocks.add(new Block(401,-300,80,80,color(240,240,240)));
}
void draw() {
basePos.x = basePos.x * 0.98f + (-player.pos.x + width/2) * 0.02f; basePos.y = basePos.y * 0.98f + (-player.pos.y + height/2) * 0.02f; translate(basePos.x, basePos.y, basePos.z); background(30); for(Block b: blocks) { b.update(); b.draw(); }
}
class Block {
PVector pos = new PVector(); PVector size = new PVector(); PVector vel = new PVector(); PVector prevPos = new PVector(); color col; //Constructor Block(float x, float y, float w, float h, color col) { pos.set(x, y, -50); size.set(w/2, h/2, 100); this.col = col; } void update() { prevPos.set(pos); pos.add(vel); } void draw() { pushMatrix(); translate(pos.x, pos.y, pos.z); fill(col); box(size.x*2, size.y*2, size.z*2); popMatrix(); } //Processing when it is in contact with the block. //Returns true if the contact. boolean isHit(Block b) { char aa=port.readChar(); println(aa); if(aa=='A'){ if(b.pos.x!=0){ return false; } } else if(aa=='B'){ if(b.pos.x!=100){ return false; } } else if(aa=='C'){ if(b.pos.x!=200){ return false; } } else if(aa=='D'){ if(b.pos.x!=300){ return false; } } else if(aa=='E'){ if(b.pos.x!=400){ return false; } } else if(aa=='F'){ if(b.pos.x!=500){ return false; } } else if(aa=='G'){ if(b.pos.x!=600){ return false; } } else if(aa=='H'){ if(b.pos.x!=700){ return false; } } else if(aa=='I'){ if(b.pos.x!=40){ return false; } } else if(aa=='J'){ if(b.pos.x!=140){ return false; } } else if(aa=='K'){ if(b.pos.x!=340){ return false; } } else if(aa=='L'){ if(b.pos.x!=440){ return false; } } else if(aa=='M'){ if(b.pos.x!=540){ return false; } } else if(aa=='N'){ if(b.pos.x!=740){ return false; } } return abs(pos.x - b.pos.x) < abs(size.x + b.size.x) && abs(pos.y - b.pos.y) < abs(size.y + b.size.y); }
}
class Player extends Block {
boolean isLanding; int jumping; //Constructor Player(float x, float y) { super(x, y, 50, 50, color(255, 0, 0)); size.z = 20; } void update() { super.update(); //Player chase mouse pointer. float x = mouseX - pos.x - basePos.x; if(abs(x) > size.x) vel.x += x < 0 ? -0.2f : 0.2f; //gravity vel.y += 0.3f; //Jumping processing if(mousePressed) { if(isLanding) { vel.y -= 3.0f; jumping = 12; } if(jumping-->0) vel.y -= 2.0f; } else jumping = 0;
vel.mult(0.98f); vel.limit(10.0f); isLanding = false; for(Block b: blocks) { if(b==this) continue; if(isHit(b)) { println(b.pos.x); b.col = color(random(255),random(255),random(255)); //processing player touched blocks if(prevPos.y > b.pos.y && pos.y - size.y <= b.pos.y + b.size.y) { pos.y = size.y + b.pos.y + b.size.y; } else if(pos.y + size.y >= b.pos.y - b.size.y) { pos.y = -size.y + b.pos.y - b.size.y; isLanding = true; //if keyPressed,player transfer coordinate if(keyPressed){ if(key=='a') pos.y=0; if(key=='s') pos.y=370; } } } if(keyPressed){ if(key=='r'){ pos.y=-500; pos.x=400; } } } }
}
・PSoCのソースプログラム(ディスプレイ鍵盤)
#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules
int PW=125; // Pulse Width
int SILENT=0, A=229, B=204,C=182,D=172,E=153,F=136,G=121,H=115,I=217,J=193,K=162,L=145,M=129,N=108; //Scale
void main(void)
{
char * strPtr; // Parameter pointer UART_CmdReset(); // Initialize receiver/cmd buffer UART_IntCntl(UART_ENABLE_RX_INT); // Enable RX interrupts Counter8_WritePeriod(155); // Set up baud rate generator Counter8_WriteCompareValue(77); Counter8_Start(); // Turn on baud rate generator UART_Start(UART_PARITY_NONE); // Enable UART M8C_EnableGInt ; // Turn on interrupts PWM16_1_Start(); UART_CPutString("\r\nPSoC Synthesizer V1.1 \r\n"); while(1) { if(*strPtr = UART_cGetChar()) { if (*strPtr=='a') PWM16_1_WritePeriod(A); else if (*strPtr=='b') PWM16_1_WritePeriod(B); else if (*strPtr=='c') PWM16_1_WritePeriod(C); else if (*strPtr=='d') PWM16_1_WritePeriod(D); else if (*strPtr=='e') PWM16_1_WritePeriod(E); else if (*strPtr=='f') PWM16_1_WritePeriod(F); else if (*strPtr=='g') PWM16_1_WritePeriod(G); else if (*strPtr=='h') PWM16_1_WritePeriod(H); else if (*strPtr=='i') PWM16_1_WritePeriod(I); else if (*strPtr=='j') PWM16_1_WritePeriod(J); else if (*strPtr=='k') PWM16_1_WritePeriod(K); else if (*strPtr=='l') PWM16_1_WritePeriod(L); else if (*strPtr=='m') PWM16_1_WritePeriod(M); else if (*strPtr=='n') PWM16_1_WritePeriod(N); else if (*strPtr=='o') PWM16_1_WritePeriod(SILENT); } }
}
・Processingのソースプログラム(ディスプレイ鍵盤)
import processing.serial.*;
String psocPort = Serial.list()[Serial.list().length-1];
Serial port=new Serial(this,psocPort,38400);
ArrayList<Block> blocks = new ArrayList<Block>();
Player player;
PVector basePos = new PVector();
int jumpcount=0;
void setup() {
size(800, 600, P3D); background(30); blocks.add(player = new Player(100, 100)); for(int i=0; i<8; i++) { if(i!=2 && i!=6) blocks.add(new Block(i*100+40,80,80,80, color(0))); blocks.add(new Block(i*100,500,80,80,color(240,240,240))); } blocks.add(new Block(401,-300,80,80,color(240,240,240)));
}
void draw() {
basePos.x = basePos.x * 0.98f + (-player.pos.x + width/2) * 0.02f; basePos.y = basePos.y * 0.98f + (-player.pos.y + height/2) * 0.02f; translate(basePos.x, basePos.y, basePos.z); background(30); for(Block b: blocks) { b.update(); b.draw(); }
}
class Block {
PVector pos = new PVector(); PVector size = new PVector(); PVector vel = new PVector(); PVector prevPos = new PVector(); color col; //Constructor Block(float x, float y, float w, float h, color col) { pos.set(x, y, -50); size.set(w/2, h/2, 100); this.col = col; } void update() { prevPos.set(pos); pos.add(vel); } void draw() { pushMatrix(); translate(pos.x, pos.y, pos.z); fill(col); box(size.x*2, size.y*2, size.z*2); popMatrix(); } //Processing when it is in contact with the block. //Returns true if the contact. boolean isHit(Block b) { return abs(pos.x - b.pos.x) < abs(size.x + b.size.x) && abs(pos.y - b.pos.y) < abs(size.y + b.size.y); }
}
class Player extends Block {
boolean isLanding; int jumping; //Constructor Player(float x, float y) { super(x, y, 50, 50, color(255, 0, 0)); size.z = 20; } void update() { super.update(); //Player chase mouse pointer. float x = mouseX - pos.x - basePos.x; if(abs(x) > size.x) vel.x += x < 0 ? -0.2f : 0.2f; //gravity vel.y += 0.3f; //Jumping processing if(mousePressed) { if(isLanding) { vel.y -= 3.0f; jumping = 12; } if(jumping-->0) vel.y -= 2.0f; } else jumping = 0;
vel.mult(0.98f); vel.limit(10.0f); isLanding = false; for(Block b: blocks) { if(b==this) continue; if(isHit(b)) { println(b.pos.x); if(b.pos.x==0){ port.write(97); } else if(b.pos.x==100){ port.write(98); } else if(b.pos.x==200){ port.write(99); } else if(b.pos.x==300){ port.write(100); } else if(b.pos.x==400){ port.write(101); } else if(b.pos.x==500){ port.write(102); } else if(b.pos.x==600){ port.write(103); } else if(b.pos.x==700){ port.write(104); } else if(b.pos.x==40){ port.write(105); } else if(b.pos.x==140){ port.write(106); } else if(b.pos.x==340){ port.write(107); } else if(b.pos.x==440){ port.write(108); } else if(b.pos.x==540){ port.write(109); } else if(b.pos.x==740){ port.write(110); } else{ port.write(111); } b.col = color(random(255),random(255),random(255)); //processing player touched blocks if(prevPos.y > b.pos.y && pos.y - size.y <= b.pos.y + b.size.y) { pos.y = size.y + b.pos.y + b.size.y; } else if(pos.y + size.y >= b.pos.y - b.size.y) { pos.y = -size.y + b.pos.y - b.size.y; isLanding = true; //if keyPressed,player transfer coordinate if(keyPressed){ if(key=='a') pos.y=0; if(key=='s') pos.y=370; } } } if(keyPressed){ if(key=='r'){ pos.y=-500; pos.x=400; } } } }
}
考察
この音感試しゲームを作成するにあたって、pwm_uart_2のラボとチーム1333のPSoC鍵盤を参考にした。スイッチ基盤からの入力はPSoC鍵盤のソースプログラムを基に、音の出力だけでなくLCDへの出力とシリアル通信も行うようにした。また、スイッチを押してから鍵盤の画面の当たり判定が消えるまでのタイムラグを初めはProcessingで行おうとしたが、その方法でタイムラグを作ろうとすると画面が停止してしまったため、音を出力してから文字を送信するまでの間に時間差を設けることでこれを実現した。
Processingでは鍵盤を模した画面とプレイヤーが操作するブロックを表示し、通常は鍵盤の上にいるが、音が鳴らされてから文字が送信されるまでの間にその音階に該当する鍵に移動しないと、文字が送信されたときにその鍵にオブジェクトがいない場合、鍵とオブジェクトの当たり判定が消えて落ちるようになっている。
今回の方法では通信は行われるものの音が途切れないという理由で連続して同じスイッチを押してはいけない、通信先の画面に入力した文字が表示されるまでスイッチを押し続け、文字が表示されたらスイッチから指を離さなければならないなど、ゲームを行うにおいて制約が多い。そのため、音を鳴らす処理と文字を送信する処理の順序や処理の方法を工夫をして、同じスイッチを連続して押しても良いようにしたり、スイッチを押し続けていなくても連続して文字を送信できるようにしたりする必要があると考えられる。
Processingを利用しているが、シリアル通信の部分でPSoCとの通信がうまくいかなかった。これはProcessingではchar型の文字しか送ることはできなかったためPSoCでもcharで受け取らなければならなったが、PSoCでは終端文字などを考慮して動作をしていたためここの部分で齟齬が出てしまった。
Processingの記述についてだが、これはblockというclassを作り、これを継承することでプレイヤーとオブジェクトを分けている。また当たり判定についてはisHitというメソッドで判定をしている。これによりどのオブジェクトに接しているかなどを判定したが、この判定はまだ少しバグを含んでいて、オブジェクトの中にプレイヤーが入ってしまうと当たり判定が消失してしまうようなバグが見ることができた。これを解消するためにはオブジェクトの中にプレイヤーの座標があった時には位置をずらしたり、着地しているなどの判定にすることで回避することが可能である。
今までのラボを参考にして作成をしたが、Processingを使うラボがなかったためいろいろな実験をしながら作成をしたが、PSoCとProcessingの間では少しタイムラグがあるため思わぬ動作をしてしまうことが多々あった。たとえば、PSoC側でスイッチを話しているにも関わらずProcessingのほうではずっと押されている判定になっていたり、判定などを高速にしてしまうと正しい情報が送られずに飛び飛びの情報をPSoCが読み込んでしまって間違った出力をしてしまうなどのことがあった。これを解消するためにはシリアル通信をするときには一瞬の通信をするのではなく、少しの間送り続けるようにして、ハザードをおきにくくすることで誤動作を減らすことができる。
Processingでコードを作成しているときに、オブジェクトが移動しても背景にそのまま絵が残ってしまうような現象を見ることができた。これは背景の更新をしていないため前の情報がそのまま画面に残ってしまうとということが原因だと思われる。これを解消するために処理を行うごとに背景を塗りつぶすことで前の絵を残さずにあたかも動いているように見せることができた。しかしこれでは処理が重くなってしまう。これを解決するためには裏画面処理というものをすると処理を遅くせずにすむと考えられる。裏画面処理とは画面に表示されているものの次の動作を画面に表示せずに作成をし、そのタイミングが来たら表の画面と裏の画面を取り換える。その次にまた裏画面を次に変えることで動いているように見える上に処理を高速化することができる。
- 最終更新:2014-07-15 15:17:41