80サ終したアーケードゲームを遊びたい[Gunslinger Stratos]
お気に入りのゲームのサ終は悲しいものです。
好きだったゲームはいつでも遊べるように保存したいと思いますよね?
頻繁に遊ぶわけではないけど、ふとした瞬間に遊びたくなるとき、ありますよね。
というわけで、中古で出回っているアーケードゲームをなんとかコンパクトに再現できないか、という試みをしてみました。
今回は、スクウェア・エニックスの『Gunslinger Stratos』(通称ガンスト)というゲームを対象にしています。
こういった感じの、まさにアーケードの醍醐味を味わえる大型筐体のガンシューティングゲームです。
実は、中古の筐体を買えばそのまま動くらしいです(ただし当然オフラインモード=練習モードのみ)。
サ終したアーケードゲームは商用の需要がなくなるため、個人でも買える価格帯で出回っていることが多いです。
物によっては10万円以下で購入可能で、個人向けに販売してくれる中古業者もあります。送料のほうが高くなることもありますが…。
とはいえ、格ゲーに代表されるような小さい(?)筐体とかならともかく、こんな大きな筐体を家に置いておく場所はありませんね。
そこで、このゲームを遊ぶために必要なのは、実際のところ、大きなディスプレイとガンコンだけでは?ということで、なんとか中身だけを仕入れてコンパクトに再現する試みです。
用意すべき実機
-
メイン基板(TAITO Type X3)
ゲーセン用語です。ここを見ているような電子回路エンジニアの方には、これは基板じゃなくてPCだろ、とツッコミたいと思いますが、本記事中でも「基板」と記載しますので、このPCのことを指しているのか、「板そのもの」を指しているのかは文脈で判断してください。
実際には、中身はWindowsPCで、専用IOなどが少しついているくらいです。
以前は中古で大量に出回ってました。2万円くらいで買いました。サ終が2021年なので、現在では流通量は少ないようです。
ゲームプログラム自体は内蔵されているHDDに入っています。HDDとドングルだけで出回っていることもありますが、どっちみち専用PC本体も必要です。HDDはATAセキュリティロックがかかっているので、HDDとドングルが手に入っても普通のPCでは動きませんし、(このPCのBIOSが無いと)読み出しもできません。
メイン基板はガンスト2とガンスト3で同じものです。2と3の両方を遊びたい人は、メイン基板、ガンスト2のHDD+ドングル、ガンスト3のHDD+ドングル、を入手すると差し替えて遊べます。
ガンスト初代は試していません。ちなみにガンスト2時代に始めたので、本物の初代もプレイしたことはありません。 -
ガンコン
2丁拳銃するゲームなので左右分1セット必要です。
こちらも本物の中古です。メイン基板以上になぜか出回ってました。
赤外線センサーを使って位置検出しています。正直動作理論がよくわかってません。少なくともWiiリモコンのようなカメラ検出ではないようです。
たぶん自作は難しいです。というか形が変わったら別ゲーですので、たとえ互換品が作れたとしてもこれは何としても本物を手に入れたいです。 -
ガンコン制御基板
OMC-GZH200HS M/Sという基板が2枚必要です。
こちらも本物の中古です。動作理論がわかれば作れそうな気もしますが、結構アナログ的な動きをしてそうな気配があります。
DIPスイッチがありますが、1:OFF/2:ONがマスター側、OFF/OFFがスレーブ側です。 -
IR LED発光基板一式
メーカー品番はK91X1228のようです。10個必要です。
シフトレジスタが構成されていて、シリアルに各ボードがつながって、個別にLEDを制御できます。たぶん頑張れば作れます。
回路図はこんな感じだと思います(動作未確認)。
-
IO基板
TAITO FAST IO UNIVERSAL P.C.B J9100633A/K91X1234Cという基板です。
こちらも本物の中古です。サービスメニュー設定用スイッチなどがここにつながります。頑張れば作れるかもしれません。
DIPスイッチがありますが、用途はよくわかりません。1:OFF/2:ONで動いてます。 -
USB-RS232Cコンバーター
対応するコンバーターはVID=0557/PID=2008で固定されています。ATEN USB to Serial Bridgeという製品のようです。
やや特殊ですが、中身はPL2303なので、VID/PID書き換えツールを使えば秋月などの安価なPL2303の変換ケーブルを使用できます。
PL2303はWindows11非対応のようなので、今更あまり使い道もなく、安く入手できます。
不要なもの
- ボイスチャット系基板
このゲームはボイスチャットができます。ボイスチェンジャー機能もあり、どうやら外部基板でそのあたりの制御をしているようですが、ゲームの起動には不要です。
作るもの概要
-
カードリーダーエミュレーター
カードリーダーが無いとエラーで起動しないのですが、カードリーダーが手に入らなかったので偽装してます。 -
ハーネス一式
各部材を接続するためのケーブルです。
詳細
カードリーダーエミュレーター
概要
主にこの記事のメインです。
アーケードゲームのメイン基板は、主に周辺のIO基板などとJVSという統一規格で接続されています。電気的/プロトコル的にはメーカー間で互換性があることになっています。
JVSは1997年ころから使われた規格で、コントロールパネルの入力に使われるほか、カードリーダーやLED電飾、その他ギミックの制御にも使われています。今となってはそれほど早くないシリアル通信で、すべてのIO基板がデイジーチェーンで接続されます。
規格が作られた時代に比べ、コントロールパネル以外の周辺制御が豪華になり、速度が足りない、ということで、TAITOはこのゲームが使われていたころはJVSではなくFAST I/Oという別規格を独自にメインに採用しています。
過去資産の流用なのか、このゲームでもカードリーダー制御にだけJVSが使用されています。ガンコンのボタンなどはFAST IOを使用しているようです。
JVSは電気的にはRS485というシリアル通信になります。+/-の作動信号1ペア+接続検出信号+GNDの4芯です。半二重通信です。
物理的なコネクタはUSBと同じものを使用します。詳細については下記参照資料に記載されています。
参考:その辺のお話→アーケードゲーム機の入出力環境の変遷と進化とは? タイトーが取り組んだ“高速I/O”【CEDEC 2014】 - ファミ通.com
参考:JAMMA VIDEO規格(第3版)※今は公開されていないのでリンク先はWebArchiveです。
レベルコンバータ
こちらについては、
TTL-RS-485モジュールコンバータ https://www.amazon.co.jp/dp/B083TTTGV8
とかを使うととくに難しくもなくArduinoなどに接続できます。
今回はArduino Pro Microに繋いで使う前提です。
Arduino用ソース
JVSプロトコルを実装して、ダミーのカードリーダー応答を返します。
JVSプロトコルの詳細は、前述の「JAMMA VIDEO規格」を参照してください。
ダミー応答なので、存在チェックをパスするのみの最低限の応答だけ実装しています。実際にはカードを読み込んだ時にカードIDを返したりはするはずですが、そのあたりは実装していません。
そもそもオフライン稼働ですので、カードを読み込む機能を実装しても動作確認もできません。
JVSエミュ
const int LED_PIN = 17;
const int DE_PIN = 5;//0:IN 1:OUT
const int SENSE1_PIN = 7;//0:0V Z:open
const int SENSE2_PIN = 8;//0:O.C. 1:2.5V
int mynode=256;
void sence(int s){
if (s==0){//0V
digitalWrite( SENSE1_PIN, LOW );
pinMode(SENSE1_PIN, OUTPUT);
}
else if(s==1){//2.5V
pinMode(SENSE1_PIN, INPUT); //open
digitalWrite( SENSE2_PIN, HIGH);
}
else if(s==2){//5V(open)
pinMode(SENSE1_PIN, INPUT); //open
digitalWrite( SENSE2_PIN, LOW);
}
}
void setup() {
Serial.begin (460800); //PC(USB)
Serial1.begin(115200); //JVS(GP0/GP1)
pinMode(DE_PIN, OUTPUT); //0:IN 1:OUT
pinMode(SENSE2_PIN, OUTPUT); //
pinMode(LED_PIN, OUTPUT);
sence(1);//初期状態で2.5V
digitalWrite( DE_PIN, LOW );
digitalWrite( LED_PIN, HIGH );
Serial.println("START");
}
void loop() {
char buf1[60];
int cnt=256;
bool esc=false;
int incomingByte;
unsigned char packet[256];
unsigned char node;
unsigned char sum=0;
int len=-1;
int len2;
while(1){
if (Serial1.available() > 0) {
incomingByte = Serial1.read();
if (incomingByte==0xe0){
Serial.println();
cnt=0;
sum=0-0xe0;
}
if (incomingByte==0xd0){
esc=true;
}else{
if (esc){
incomingByte=incomingByte+1;
}
esc=false;
if (len!=0){
sum+=incomingByte;
}
sprintf(buf1, " %02x", incomingByte);
Serial.print(buf1);
if (len==0){
if (sum==incomingByte){
Serial.print(" SUM OK");
if ((node==mynode)||(node==0xff)){
pktparser(node,len2-1,packet);
}
}else{
Serial.print(" SUM NG");
}
}
if (cnt==1){
node=incomingByte;
}
if (cnt==2){
len=incomingByte;
len2=incomingByte;
}
if (cnt>2){
packet[cnt-3]=incomingByte;
}
len--;
cnt++;
}
}
}
}
void pktparser( unsigned char node,int len,unsigned char * packet){
unsigned char obuf[256];
char buf1[64];
int idx=0;
int cmdlen=0;
sprintf(buf1, "\n pktparser(%d)\n", len);
Serial.print(buf1);
while(1){
int ret=cmdparser(node,len,&packet[idx],&cmdlen,&obuf[cmdlen]);
if (ret==0){return;}//エラートラップ
idx=idx+ret;
if (idx>=len){break;}
}
sprintf(buf1, " result byte(%d)\n", cmdlen);
Serial.print(buf1);
if (cmdlen!=0){
digitalWrite( DE_PIN, HIGH );
Serial1.write(0xe0);//SYNC
Serial1.write(0x00);//NODE
Serial.print(" result-- e0 00 ");
Serial1.write(cmdlen+2);//LEN
sprintf(buf1, "%02x ", cmdlen+2);
Serial.print(buf1);
Serial1.write(0x01);//PACKET STATUS
Serial.print("01 ");
unsigned sum=cmdlen+2+1;
for(int i=0;i<cmdlen;i++){
Serial1.write(obuf[i]);
sprintf(buf1, "%02x ", obuf[i]);
Serial.print(buf1);
sum=sum+obuf[i];
}
Serial1.write(sum);//SUM
sprintf(buf1, "%02x ", sum);
Serial.print(buf1);
Serial.println("--");
Serial1.flush();
digitalWrite( DE_PIN, LOW );
}
Serial.println(" parse end");
}
//戻り値はパースしたコマンドのバイト数
//obufにステータスを詰めて、cmdlenを更新する
int cmdparser(unsigned char node,int len,unsigned char * packet,int * cmdlen,unsigned char * obuf){
char buf1[64];
unsigned char cmd=packet[0];
int ret=0;
switch (cmd){
case 0xf0: //リセット
if (packet[1]==0xd9){
Serial.println(" cmd f0d9");
sence(1);
digitalWrite( LED_PIN, HIGH );
mynode=256;
ret=2;
}
break;
case 0xf1: //アドレス設定
Serial.println(" cmd f1");
mynode=packet[1];
ret=2;
obuf[0]=0x1;//REPORT;
*cmdlen=*cmdlen+1;
sence(0);
digitalWrite( LED_PIN, LOW );
break;
case 0x10: //I/O ID 応答例:"NAMCO LTD.;I/O PCB-1000;ver1.0;for domestic only,no analog input"
Serial.println(" cmd 10");
ret=1;
obuf[0]=0x1;//REPORT;
obuf[1]=0x41;
obuf[2]=0x42;
obuf[3]=0x43;
obuf[4]=0;
*cmdlen=*cmdlen+5;
break;
case 0x11: //コマンドREV rev1.3=0x13
Serial.println(" cmd 11");
ret=1;
obuf[0]=0x1;//REPORT;
obuf[1]=0x13;
*cmdlen=*cmdlen+2;
break;
case 0x12: //JAMMA VIDEO REV rev3.0=0x30
Serial.println(" cmd 12");
ret=1;
obuf[0]=0x1;//REPORT;
obuf[1]=0x30;
*cmdlen=*cmdlen+2;
break;
case 0x13: //通信Ver ver1.0=0x10
Serial.println(" cmd 13");
ret=1;
obuf[0]=0x1;//REPORT;
obuf[1]=0x10;
*cmdlen=*cmdlen+2;
break;
case 0x14: //機能チェック
Serial.println(" cmd 14");
ret=1;
obuf[0]=0x1;//REPORT;
obuf[1]=0x10; //0x10=カードシステム
obuf[2]=0x01; //カードのスロット数=1
obuf[3]=0x00;
obuf[4]=0x00;
obuf[5]=0x00;
*cmdlen=*cmdlen+6;
break;
case 0x26: //汎用SW入力
Serial.println(" cmd 26");
ret=2;
obuf[0]=0x1;//REPORT;
obuf[1]=0x00;
*cmdlen=*cmdlen+2;
break;
case 0x61: //不明
Serial.println(" cmd 61");
ret=4;
obuf[0]=0x1;//REPORT;
*cmdlen=*cmdlen+1;
break;
default:
sprintf(buf1, " cmd %02x unsupported\n", cmd);
Serial.print(buf1);
break;
}
if (len>=ret){
return len;
}else{
return 0;//エラートラップ
}
}
ハーネス一式
各ボード間の接続はこうなります。コネクタのpinアサインは後述
オーディオ端子(緑とピンク)は何か差し込んでおかないと未接続でエラーになります。
IR LED発光基板一式はディスプレイの周りに配置しますが、このように配置します。横方向は等間隔のようです。
ディスプレイ正面から見た配置図です。
各ケーブルのピンアサイン
※1:ガンコン制御基板間
ガン制御基板のCN5同士を接続します。JST XHコネクタです。
2pinから5pinが1対1結線です。1pinは未接続です(接続すると動かない)。
※2:ガンコン制御基板→ガンコンL
ガンコン制御基板のGUN1Pとガンコンを接続します。
ガンコン制御基板はJST XHコネクタ、ガンコン側はJST YLコネクタです。
※3:ガンコン制御基板→ガンコンR
ガンコン制御基板のGUN1Pとガンコンを接続します。
ガンコン側はガンコンLと同様、JST XHコネクタです。
※4:ガンコン制御基板→IR transmitter
ガンコン制御基板のLED(コネクタ)と、IR transmitter基板のCN1を接続します
JST XHコネクタです。
※5:電源
JST XLコネクタです。
※6:IO関連
IO基板側は通常の2.54mmピッチピンヘッダです。
バラ線で接続できるコネクタであれば、2keyなので、
ヒロセのHIF3BA-60D-2.54C(63)あたりです。
※7:IR transmitter→IR transmitter
IR transmitter基板のCN1と次のIR transmitter基板のCN2を接続します
JST XHコネクタです。
全pin 1対1結線です
組み立て
扱いやすいように組み立てます。
合板の上に必要な基板類を全部固定しました。
とはいえ、この先にガンコンがつながるので、急に引っ張ったりすると色々致命的なダメージを受けそうな気はしますね。
できれば丈夫なケースに入れて、ガンコンのケーブルを引っ張ってもIO基板などに直接力が加わらない様な作りにしたほうがよさそうです。
IR transmitterは、前述のとおり各種接続をし、ディスプレイを囲むようにを配置すれば準備完了です。
適当にあり合わせで作りました。配線用のモールにIR送信基板を止めて、上下はリボンでぶら下げるようにしています。
とりあえず動作確認なのでモールにテープで止めてあるだけという超適当ですが、基板にねじ穴もありますし、綺麗にねじ止めして、ケーブルもモールの中を通すようにするとそこそこ綺麗にはなると思います。
遊ぶ
全体図はこんな感じになります。結構カオスですね。業務用機なのでそこは仕方ありません。
チュートリアルが結構たくさんあるので、それなりにオフラインでも遊べます。
これでサ終を気にせず、壊れるまでずっと遊ぶことが出来ます(ただしチュートリアルだけ)。
2セット用意してもローカル対戦とかはできないと思います。ローカル対戦ができればオフラインでもかなり遊べると思うんですけどね。
投稿者の人気記事
![サ終したアーケードゲームを遊びたい[Gunslinger Stratos]](https://res.cloudinary.com/elchika/image/upload/t_elchika_article_cover/v1/user/eda640be-0a23-4b3f-b99b-f5269d4c9664/article/f669aaf9-cfb9-46b3-9a9c-7bd945a5e8a7/yma9kcxqzdtu8vigivvt.jpg)

-
lyricalmagical
さんが
前の火曜日の1:51
に
編集
をしました。
(メッセージ: 初版)
-
lyricalmagical
さんが
前の火曜日の1:52
に
編集
をしました。
-
lyricalmagical
さんが
前の火曜日の2:01
に
編集
をしました。
ログインしてコメントを投稿する