Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

React 画面遷移用スクリプト作成

以前Reactの画面遷移方法についてまとめました。
elsammit-beginnerblg.hatenablog.com


ただ、画面遷移用にファイルを複数作成したりコード書くのが結構手間だったりするんですよね。
だからって別ファイルを持ってくると余計なコードを削除したりしなければならず、これも手間。。。

ということで、画面遷移までを簡単にセッティングできるスクリプトを作成しました!!


スクリプト格納先
https://github.com/Elsammit/React_SettingShell.git

■使い方
前提として、yarnがインストールされていることです。

格納されているreact.shを実行すればOKです。
引数ですが、
 ・第1引数:Reactプロジェクト名
 ・第2引数以降:各ページ用コード
になります。
例えば、

react.sh rpg aaa bbb ccc

と実行すると、
rpgというプロジェクトが作成され、
aaaとbbb、cccというページが作成されます。
App.jsには画面遷移用のパスが格納されており、コードとしてはこちらのようになります。

import { BrowserRouter, Route } from 'react-router-dom'
import ccc from './ccc'
import bbb from './bbb'
import aaa from './aaa'

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <div>
<Route path='/ccc' component={ccc}/>
<Route path='/bbb' component={bbb}/>
<Route path='/aaa' component={aaa}/>
        </div>
      </BrowserRouter>
    </div>
  );
}

export default App;

それぞれ作成されるページには、

Hello world

というメッセージが表示されます。
※全て同じページが表示されます。

■使用上の注意
App.jsを見て頂ければわかりますが、、、
ルートパスである、

<Route path='/' component={ファイル名}/>

が存在しません。
必要に応じて修正をお願いいたします!!

また、インデントも一部ズレてしまっております。
こちらも気になる方は修正お願いいたします。

シェルスクリプト解説
シェルスクリプトですが、大きく分けて3つに分かれておりす。
 ①プロジェクト作成
 ②ページ用ファイルの追加・修正
 ③react-router-domインストール

まずは①のプロジェクト作成です。
コードはこちらのようになっており、第1引数でプロジェクトを作成です。

create-react-app $1

次に②です。
コードはこちらになります。

rm $1/src/App.js
cp App.js ./$1/src/

base=$((6+$#))
for x in "$@"
do
    if [ $x = $1 ]; then
        echo "same var"
    else
        cp page1.js ./$1/src/$x.js
        touch ./$1/src/$x.css

        sed -i -e "2d" ./$1/src/$x.js
        sed -i -e "1a import './"$x".css'" ./$1/src/$x.js
        sed -i -e "4d" ./$1/src/$x.js
        sed -i -e "3a export default class "$x" extends Component  {" ./$1/src/$x.js

        sed -i -e "1a import "$x" from './"$x"'" ./$1/src/App.js
    fi
done

for x in "$@"
do
    if [ $x = $1 ]; then
        echo "same var"
    else
        sed -i -e $base"a           <Route path='/"$x"' component={"$x"}/>" ./$1/src/App.js
    fi
done

デフォルトのApp.jsを削除し、こちらが用意したApp.jsに置き換えます。
さらにこちらの用意したpage1.jsを引数にて指定するページ名でコピーを行います。

そしてそして、

        sed -i -e "2d" ./$1/src/$x.js
        sed -i -e "1a import './"$x".css'" ./$1/src/$x.js
        sed -i -e "4d" ./$1/src/$x.js
        sed -i -e "3a export default class "$x" extends Component  {" ./$1/src/$x.js

        sed -i -e "1a import "$x" from './"$x"'" ./$1/src/App.js

にてテキストに対する指定する行の削除と挿入を行っております。

最後に③!!react-router-domのインストール。

yarn add install react-router-dom

■最後に
これでセットアップが楽になった!!かな?
自分で使っていき、より使いやすくカスタマイズできればいいな!!と考えております。

もし使われて、要望・不満等ございましたらご連絡お願いします。

2021年の抱負 今年もよろしくお願いします!!

明けましておめでとうございます!!

三箇日は一旦PCや勉強から離れてのんびりと過ごしておりました。
リフレッシュは大切ですね!!

ですが、、、
それも今日でおしまい。
バリバリ、アプリ作成や勉強を進めていきたいと思います。
今年も引き続き技術系のブログを上げていきたいと思っております!!
よろしくお願いいたします!!

新年一回目のブログなので、今年1年の抱負や実施項目を述べておこうと思います。



■抱負

今年は3本柱で!!
 ①自作アプリを作成し収益を得る
 ②副業を始める
 ③月15件以上ブログを書く

もちろん家庭を大切にすることが大前提です!!

①はWebアプリでのリリースを考えております。

■課題

③は去年からの継続なので、、、なんとかなりそう。
②もクラウドワークスに登録済みで案件に応募していけばいいかな??


①は正直何から始めればよいのか分からずしんどい😅

何がしんどいのか、、、
・どのように利益を出していくのいか、
 マーケティングやマネタイズの知識・経験が皆無

■達成のための方針

ということで、
ちょっとマーケティングやマネタイズの勉強も進めていこうかな?と思います!!
まだまだ初心者も初心者ですので、
 ・本を色々読んで知識をつけていく
 ・他のアプリ開発者のブログや情報を見る
 ・アイデアを練る
などのことを行っていきたいな。と思います。

■最後に

本を色々読み漁っていますが、、、
マーケティングは奥が深いですね😅
読んだ本については備忘録も踏まえてブログに記載していきたいと思います。

後、アイデアを煮詰めすぎてもいいことない気がするのでまずは始めることが大事だと思って大胆に行動したいと思います。


おみくじアプリ作ってみた

今年も残すところ後1日になりました。
今年はありがとうございました!!来年もよろしくお願いいたします!!

今年はコロナに振り回された1年でした。。。
来年は終息し、平穏な一年になることを切に願います!!

一足早いですが、おみくじアプリを作成しましたので、紹介です!!
来年の運勢を占うために使って見てください。



■作成したおみくじアプリ

こんな感じになりました!!
f:id:Elsammit:20201231101233g:plain

アプリについてはこちらに公開しましたので、来年の運だめしにどうぞ!!
https://elsammit.github.io/OmikujiApp/

ソースコード

アプリはReactで作成しました。
ソースコード全体はこちらに格納しておりますので、よろしかったらどうぞ!!
https://github.com/Elsammit/OmikujiApp.git

今回はおみくじアプリ作成にあたり、回転アニメーションを使用したのでこちらを紹介します。

■画像に回転アニメーション付与

画像に回転アニメーションを付与させるためには、

【.js】
      return (
        <div className="background">
            <img className="omikuji" src={Omikuji}"/>
        </div>
      );
【css】
.omikuji{
  animation: r 0.5s linear infinite alternate;
}

@keyframes r{
  0%    { transform: rotate(0deg);}
  100%  { transform: rotate(30deg);}
}

といったように、cssとjsを作成すればOKです。
実施していることは、classNameでcssで定義するomikujiを指定し、

.omikuji{
  animation: r 0.5s linear infinite alternate;
}

にて、設定した時間(今回は0.5s)で

@keyframes r{
  0%    { transform: rotate(0deg);}
  100%  { transform: rotate(30deg);}

にて指定した角度で回転を行います。
今回は、
0.5sの間で0度~30度で回転動作を行います。

実行してみるとこんな感じになります。
f:id:Elsammit:20201231104444g:plain

keyframesをこちらのように180度に変更すると、

@keyframes r{
  0%    { transform: rotate(0deg);}
  100%  { transform: rotate(180deg);}

f:id:Elsammit:20201231104638g:plain

というように0~180度まで0.5sで回転します。

では、0.5s⇒2sに変更してみます。

.omikuji{
  animation: r 2s linear infinite alternate;
}

実際に動作させてみると、こちらのように
2sでの回転動作が実現出来ます。
f:id:Elsammit:20201231105026g:plain

■最後に

技術ブログ作成を始めて続けた1年でした。
来年も出来るだけ挙げていきたいと思いますので、今後ともよろしくお願いいたします!!



RaspberryPiでのUbuntu20.04 サーバー起動

今回はRapspberryPiでのUbuntu20.04を起動させるまでの手順を紹介いたします。



■実施理由

Raspberry Piに対して一般的にはRasberryPi OSをμSDに書き込めば簡単に環境構築が可能なのですが、
arm版のパッケージを調達して動かしてみたいな!!と思い、
調べてみたところ見かけたのでちょっとやってみました!!

【RasberryPi OS】
https://www.raspberrypi.org/software/

■環境

今回はRaspberryPi 3とRaspberryPi 4の両方で確認を実施しました。
どちらも同様の手順で実施できました!!

【国内正規代理店品】Raspberry Pi4 ModelB 4GB ラズベリーパイ4 技適対応品【RS・OKdo版】

新品価格
¥6,875から
(2020/12/30 20:17時点)


ubuntu OSダウンロード

arm版のubuntu OSですが、すでに公式が展開しておりました。
(知らなった😂)
こちらから、自分の環境にあったubuntu OSをインストールします。
https://ubuntu.com/download/raspberry-pi

今回は、
ubuntu-20.04.1-preinstalled-server-arm64+raspi.img.xz
をダウンロードしました。

その後、

xz -dv ubuntu-20.04-preinstalled-server-arm64+raspi.img.xz

を実行して圧縮されたイメージファイルを展開します。

そして、μSDを挿入し、こちらのコマンドを実行しイメージをμSDへ書き込みます。

sudo dd if=ubuntu-20.04-preinstalled-server-arm64+raspi.img of=μSDデバイスパス(例:/dev/sda) bs=1024

かなり時間がかかるので、お茶でもしながらしばし待ちます。

書き込みが完了したら、Raspberry PiにμSDを挿入し、電源ON!!

初回は、

ユーザ名:ubuntu
パスワード:ubuntu

になっておりました。
また、ログイン後パスワード設定を要求されるので、新規パスワードを設定。

これで、使用できる状態になります。

■使ってみた感想

普通にubuntuとして利用できました。
ディレクトリ構成もubuntuとそこまで変わらず、使いやすかったです。
後、使用しているストレージサイズは約2.5GB程度なので、小さいストレージでも使えそうなのが良きでした。
ただ、CUIですのでGUIで使用したい場合にはそのための設定が必要になります。

■最後に

RaspberryPiにarm版ubuntuで起動・動作させてみました。

こういうOSに近い下回り系って蔑ろにするエンジニアいるんですよね😅
別にありもの使えばよいから、上位のアプリやWeb系に注力すればいいじゃん!!っていう方。。。

だけどそれだと、後々困ることあるんですよね。。。
ありものが使えるプラットフォームにしなくちゃならなかったり、余計な制約がついちゃったりするで、
それで、CPUやメモリ使用、ストレージ管理等々実装や運用系で困ってしまうなんてことが。。。

自分は、システムを設計するための基礎知識だと思っているため、しっかり押さえていきたいと思います!!



Reactでバトル画面を作成

前回マップ画面の作成とキャラクターの操作を実施してみました。
elsammit-beginnerblg.hatenablog.com

今回はバトル画面の作成を行ってみましたので、その報告です!!
※技術の備忘録は少なめ(というかほとんどない)です。



■環境

・ubuntu16.04(VirtualBox上)
・React:17.0.1
・react-router-dom:5.2.0

※react-router-domはマップ⇔バトル画面間の遷移のために利用

■完成結果

できたアプリはこちらのようになります。
f:id:Elsammit:20201229225120g:plain

それっぽくできたかな?と思っております!!

■コード紹介

コード全体はこちらのGithubに格納しておりますので、こちらをご参照ください。
https://github.com/Elsammit/RPGGame

バトル画面はこちらのコードを参考にしました。
ただ、参考はjavascriptであるためReact用に変更しました。
https://blog.ver001.com/javascript-keydown/

重要な部分としては、こちらになります。

    componentDidMount(){
        this.setState({xx:this.props.location.state.xx});
        this.setState({yy:this.props.location.state.yy});

        document.addEventListener(
            "keydown",
            this.handleKeyDown,
        );
        this.activeMenu(1);
    }

    componentWillUnmount() {
        document.removeEventListener(
          "keydown",
          this.handleKeyDown,
        );
        menu_id = 0;
    }

    handleKeyDown = (e: KeyboardEvent) => {
        switch (e.key) {
            case "Left": // IE/Edge specific value
            case "ArrowLeft":
                break;
            case "Right": // IE/Edge specific value
            case "ArrowRight":
                break;
            case "Up": // IE/Edge specific value
            case "ArrowUp":
                if (menu_id <= 1) {
                    this.activeMenu(5);
                } else {
                    this.activeMenu(menu_id - 1);
                }
                break;
            case "Down": // IE/Edge specific value
            case "ArrowDown":
                if (menu_id >= 5) {
                    this.activeMenu(1);
                } else {
                    this.activeMenu(menu_id + 1);
                }
                break;
            case "Enter":
                this.doCommand(menu_id);
                break;
            default:
                console.log("key Error");
                break;
            }
      };

    activeMenu = (id) =>{
	    if (menu_id === id) {
		    this.doCommand(id);
	    } else {
		    if (menu_id !== 0) {
			    document.getElementById('menu' + menu_id).className = 'menu';
		    }
		    document.getElementById('menu' + id).className = 'menu menu-active';
		    menu_id = id;
	    }
    }
    
    //コマンドの実行
    doCommand = (command_id) => {
	    switch (command_id) {
		    case 1: //たたかう
			    this.DoAttack();
                break;
              case 2: //まほう
			    document.getElementById('message').innerHTML = '<span class="message">残念!  まほうを覚えていない!</span>';
                break;
		    case 3: //ぼうぎょ
			    document.getElementById('message').innerHTML = '<span class="message">AAAは みをまもっている!</span>';
			    break;
		    case 4: //どうぐ
			    document.getElementById('message').innerHTML = '<span class="message">しかし なにももっていなかった。</span>';
			    break;
		    case 5: //にげる
                document.getElementById('message').innerHTML = '<span class="message">AAAたちは戦闘から逃げた!!</span>';
                setTimeout(this.DoEscape, 2000);
                break;
		    default:
			    break;
	    }
    }

やっていることは、
componentDidMount関数にてキーイベント登録。
さらに、キーボードの矢印キーを押下すると、
handleKeyDownイベントがコールされ、選択されているカーソール位置を変更します。
カーソール位置を変更するための関数は、
activeMenu関数にて実施しています。

後はエンターキーを押下した時に、
たたかう、まほう、ぼうぎょ、どうぐ、にげる
に合わせた処理を実行しております。

今回は時間の関係で、
たたかう、にげる
のみ実装してみました!!

攻撃時のダメージ計算はこちらの通り、
こうげき-ぼうぎょ
で差分をHPから差し引いております。

    DoAttack = () =>{
        var usrparam = this.state.usrparam.slice();
        var enemyparam = this.state.enemyparam.slice();
        var damege = usrparam[2] - enemyparam[3];
        console.log("usr:"+usrparam[2]+" enemy:"+enemyparam[3]+"damege:"+damege);
        document.getElementById('message').innerHTML = '<span class="message">AAAの こうげき!  '+ damege + 'のダメージ</span>';
        if(enemyparam[0]-damege <=0){
            setTimeout(this.KnockDown, 1000);
        }else{
            enemyparam[0] -= damege;
            this.setState({enemyparam:enemyparam});
            console.log("enemyHP:"+enemyparam[0]);
            setTimeout(this.DoAtkEnemy, 1000);
        }
    }

ユーザや敵のパラメータは、

    constructor (props) {
        super(props);
        this.state = {
            usrparam:[350,100,30,30,30,30,50],      // HP,MP,Atk,Def,SpAtk,SpDef,Speed,Lv
            enemyparam:[60,5,100,10,5,5,10,1],     // HP,MP,Atk,Def,SpAtk,SpDef,Speed,Lv
        };
    }

により、配列にて管理しております。

詳しい処理内容は、こちらに載っておりますのでご参考にしてください。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com

■最後に

ReactでRPGゲームを作ってみました。
結構簡単にバトル画面も作成することが出来ました。
このレベルでも1日かかってしまう。。もう少し早くアプリ作れるようになりたいな。



ReactでRPGマップ作成

年末年始のお休みに入り、少し時間が出来ました。
何かReactでアプリを作りたいな?🤔と思い、ちょっとしたRPG作成してみようかな?と思い手を動かしてみました!!

基本的にjavascriptでのRPGアプリ作成についてはこちらのサイトに掲載されていたので、流用させていただきましたが、
React固有の部分がありましたのでこちらを中心に載せておこうと思います!!
https://original-game.com/introduction-to-javascript-character-move-on-map/



■環境

Ubuntu16.04(VirtualBox上)

■完成形

今回作成したアプリはこんな感じになりました。
f:id:Elsammit:20201228221825g:plain

※画像等も
 https://original-game.com/introduction-to-javascript-character-move-on-map/
 より一部利用させていただいております。

■マップ表示

マップ表示のためのコードはこちらになります。

var map = [
    [0, 0, 1, 0, 1, 0, 0, 0 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0],
    [0, 1, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0],
    [0, 0, 1, 1, 0, 0, 0, 1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0],
    [1, 0, 1, 0, 1, 1, 0, 0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,1 ,0 ,1 ,0],
    [0, 0, 0, 0, 0, 1, 1, 1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1 ,0],
    [0, 1, 1, 1, 0, 0, 0, 0 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
    [0, 1, 1, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,0],
    [0, 0, 0, 1, 0, 0, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
    [1, 1, 0, 1, 1, 1, 1, 1 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,0 ,1 ,1],
    [1, 0, 0, 0, 0, 0, 1, 1 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,1 ,0],
    [1, 0, 1, 1, 1, 0, 0, 0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0],
    [1, 0, 1, 0, 1, 1, 1, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,1],
    [0, 0, 1, 0, 0, 1, 0, 0 ,1 ,0 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,0 ,0],
    [0, 1, 1, 1, 0, 1, 0, 1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0],
    [0, 0, 0, 1, 0, 1, 0, 0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 ,0],
    [1, 1, 0, 1, 0, 1, 0, 1 ,1 ,0 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 ,0],
    [0, 0, 0, 1, 0, 1, 1, 1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,0 ,1 ,0],
    [0, 1, 1, 1, 0, 1, 0, 0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,1 ,0 ,1 ,1],
    [0, 1, 0, 0, 0, 1, 0, 1 ,1 ,1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,0 ,0 ,0],
    [0, 0, 0, 1, 0, 0, 0, 1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0]
];

export default class Snow extends Component  {

    componentDidMount(){
        this.intervalId = setInterval(()=>{
            this.DoCanvas();
        }, 200);
    }

    DoCanvas = () =>{
        var img = new Image();
        img.src = mapImg;

        var usrimg = new Image();
        usrimg.src = UserImg;

        var goalimg = new Image();
        goalimg.src = GoalImg;

        var canvas = document.getElementById('canvas');
        
        canvas.width=640;
        canvas.height=640;
        var ctx = canvas.getContext('2d');

        for (var y = 0; y<map.length;y++){
            for(var x=0; x<map.length;x++){
                if(map[y][x] === 0){
                    ctx.drawImage(img,0,0,32,32,32*x,32*y,32,32);
                }else{
                    ctx.drawImage(img,32,0,32,32,32*x,32*y,32,32);
                }
            }
        }
        ctx.drawImage(usrimg,0,0,32,32,0,0,32,32);
    }

    render () {
        return(
            <div>
                <canvas id="canvas"></canvas>
            </div>
        )
    }
}

少し長いコードですね。。。
ただ、やっていることは単純で、
画面表示時に実行されるcomponentDidMount関数を用いて、

    componentDidMount(){
        this.intervalId = setInterval(()=>{
            this.DoCanvas();
        }, 200);
    }

により、200msで毎にDoCanvas関数をコール。
DoCanvas関数はマップ作成用の関数であり、
canvas上に、背景マップや主人公表示、ゴール位置を

        for (var y = 0; y<map.length;y++){
            for(var x=0; x<map.length;x++){
                if(map[y][x] === 0){
                    ctx.drawImage(img,0,0,32,32,32*x,32*y,32,32);
                }else{
                    ctx.drawImage(img,32,0,32,32,32*x,32*y,32,32);
                }
            }
        }
        ctx.drawImage(usrimg,0,0,32,32,0,0,32,32);

により表示させているのみになります。

■主人公を移動させる

次に主人公を移動させるためのコードです。
抜粋するとこちらのコードになります。

    componentDidMount(){
        document.addEventListener(
            "keydown",
            this.handleKeyDown,
        );
        this.intervalId = setInterval(()=>{
            this.DoCanvas();
        }, 200);
    }

    componentWillUnmount() {
        document.removeEventListener(
          "keydown",
          this.handleKeyDown,
        );
     }
   
     handleKeyDown = (e: KeyboardEvent) => {
       switch (e.key) {
         case "Left": // IE/Edge specific value
         case "ArrowLeft":
           this.keyboardLeft();
           break;
         case "Right": // IE/Edge specific value
         case "ArrowRight":
            this.keyboardRight();
           break;
         case "Up": // IE/Edge specific value
         case "ArrowUp":
              this.keyboardUp();

            break;
         case "Down": // IE/Edge specific value
         case "ArrowDown":
              this.keyboardDown();
            break;
        default:
            break;
       }
     };
   
     keyboardRight = () => {
       var {MoveX} = this.state;
       var {MoveY} = this.state;
       if(map.length-1 > MoveX){
           if(map[MoveY][MoveX+1] === 0){
                MoveX += 1;
           }
       }
       this.setState({MoveX:MoveX})
     };

     keyboardLeft = () => {
        var {MoveX} = this.state;
        var {MoveY} = this.state;
        if(0 < MoveX){
            if(map[MoveY][MoveX-1] === 0){
                MoveX -= 1;
            }
        }
        this.setState({MoveX:MoveX})
      };

      keyboardUp = () => {
        var {MoveX} = this.state;
        var {MoveY} = this.state;
        if(0 < MoveY){
            if(map[MoveY-1][MoveX] === 0){
                MoveY -= 1;
            }
        }
        this.setState({MoveY:MoveY})
      };
 
      keyboardDown = () => {
         var {MoveX} = this.state;
         var {MoveY} = this.state;
         if((map.length-1 > MoveY)){
             if(map[MoveY+1][MoveX] === 0){
                MoveY += 1;
             }
        }
         this.setState({MoveY:MoveY})
       };

    DoCanvas = () =>{
        var img = new Image();
        img.src = mapImg;

        var usrimg = new Image();
        usrimg.src = UserImg;

        var goalimg = new Image();
        goalimg.src = GoalImg;

        const {MoveX} = this.state;
        const {MoveY} = this.state;

        var canvas = document.getElementById('canvas');
        
        canvas.width=640;
        canvas.height=640;
        var ctx = canvas.getContext('2d');

        for (var y = 0; y<map.length;y++){
            for(var x=0; x<map.length;x++){
                if(map[y][x] === 0){
                    ctx.drawImage(img,0,0,32,32,32*x,32*y,32,32);
                }else{
                    ctx.drawImage(img,32,0,32,32,32*x,32*y,32,32);
                }
            }
        }

        ctx.drawImage(goalimg,0,0,32,32,(map.length-1)*32,(map.length-1)*32,32,32);

        ctx.drawImage(usrimg,0,0,32,32,MoveX*32,MoveY*32,32,32);
    }

こちらも少し長いですね😅
重要な部分としては、キーイベントの登録とイベント取得時の各種処理の実行です。
まず、

document.addEventListener(
            "keydown",
            this.handleKeyDown,
        );

にてキーイベントを登録し、handleKeyDownにて押下されたキーを判定し、
各種矢印ボタン押下時の関数をコールさせております。

     handleKeyDown = (e: KeyboardEvent) => {
       switch (e.key) {
         case "Left": // IE/Edge specific value
         case "ArrowLeft":
           this.keyboardLeft();
           break;
         case "Right": // IE/Edge specific value
         case "ArrowRight":
            this.keyboardRight();
           break;
         case "Up": // IE/Edge specific value
         case "ArrowUp":
              this.keyboardUp();

            break;
         case "Down": // IE/Edge specific value
         case "ArrowDown":
              this.keyboardDown();
            break;
        default:
            break;
       }
     };

各種矢印ボタン押下時のcase文に実行したい処理を記載すれば、OKです。
今回は、主人公を動かしたいので、XY座標を加算・減算しております。

■まとめ

今回のRPGゲームのソースコードはこちらに格納しておりますので、全体を確認したい方はこちらをご参照ください。
https://github.com/Elsammit/RPGGame

マップ移動まで作成できたので、次はバトル画面の作成ですね!!
年末年始中に作成してみたいと思います。



Reactでのマウス操作で画像位置変更

前回クリスマスを理由に雪を降らせたWebアプリを作成しました。
elsammit-beginnerblg.hatenablog.com

こちらのWebアプリに表示させていた画像位置を簡単に変更させたいな!!
出来れば、マウス操作で位置変更できると便利そうだな!!
と思い、マウス操作で画像位置変更方法を調べてみましたので、こちらをまとめようと思います。



■環境

今回はこちらの環境で動作させました。
・Ubuntu16.04(VirtualBox上)

■マウス操作での画像位置移動

まずはマウスボタン押下して画像を移動させる方法について、です。
紹介する方法を実行するとこちらのような結果となります。
f:id:Elsammit:20201227213816g:plain

ではこちらのようにマウス操作で画像を移動させるためのコードはこちらになります。
※必要なコードのみ抜粋します。

export default class Snow extends Component  {

    constructor (props) {
        super(props);
        this.state = {
            Tide:Tide,
            image_X:830,
            image_Y:550,
            x: 0,
            y: 0,
            isDrag: false,
        };
        this.handleDown = this.handleDown.bind(this);
        this.handleMove = this.handleMove.bind(this);
        this.handleUp = this.handleUp.bind(this);
    }

    handleDown = (e) =>{
        const item = this.node;
        const x = e.pageX - item.offsetLeft;
        const y = e.pageY - item.offsetTop;
        this.setState({ isDrag: true, x, y });
    }

    handleMove = (e) => {
        if (this.state.isDrag) {
            e.preventDefault();
            this.setState({
                image_Y: e.pageY - this.state.y,
                image_X: e.pageX - this.state.x,
            });
        }
    }

    handleUp = () => {
        this.setState({ isDrag: false })
    }

    render() {
        const {image_X} = this.state;
        const {image_Y} = this.state;
        const {Tide} = this.state;

        var ImX = image_X;
        var ImY = image_Y;
        return (
            <div>
                <div>
                    <img src={Tide} alt="Tide" id="TideImg" className="Tide" style={{left:ImX, top:ImY}}  onMouseDown={this.handleDown}
                          onMouseMove={this.handleMove}
                          onMouseUp={this.handleUp}
                          onMouseLeave={this.handleUp}
                          ref={(node) => { this.node = node; }} />
                </div>
            </div>
        );
    }
  }

では、コードの紹介です。
まずは画像を表示させるタグに対して、
”onMouseDown”、"onMouseMove"、"onMouseUp"、"onMouseLeave"
を定義します。
マウスのクリック中かマウス移動中かを判定するためのイベント定義ですね。

<img src={Tide} alt="Tide" id="TideImg" className="Tide" style={{left:ImX, top:ImY}}  onMouseDown={this.handleDown}
       onMouseMove={this.handleMove}
       onMouseUp={this.handleUp}
       onMouseLeave={this.handleUp}
        ref={(node) => { this.node = node; }} />

それぞれマウスイベント発行時の動作はこちらの通りになります。
【マウス移動】

    handleMove = (e) => {
        if (this.state.isDrag) {
            e.preventDefault();
            this.setState({
                image_Y: e.pageY - this.state.y,
                image_X: e.pageX - this.state.x,
            });
        }
    }

isDragフラグがON中であればマウスの移動に合わせて、
image_Y、image_Xの位置を変更させております。
image_Y、image_Xは画像のXY位置を示すデータとなります。

【マウスクリック】

    handleDown = (e) =>{
        const item = this.node;
        const x = e.pageX - item.offsetLeft;
        const y = e.pageY - item.offsetTop;
        this.setState({ isDrag: true, x, y });
    }

    handleUp = () => {
        this.setState({ isDrag: false })
    }

isDragをクリック時にはON、外した時はOFFし、
クリック時にクリックした時の画像位置を保存しています。

■マウスクリック時のクリック位置に画像移動

次にマウスクリック時の位置指定になります。
こんな感じの動作になります。
f:id:Elsammit:20201227215821g:plain

コードはこんな感じです。

export default class Snow extends Component  {

    constructor (props) {
        super(props);
        this.state = {
            Tide:Tide,
        };
    }

    testClick = (e) =>{
        var element = document.getElementById( "TideImg" );
        var width = element.naturalWidth;
        var height = element.naturalHeight;
        this.setState({image_X:e.screenX });
        this.setState({image_Y:e.screenY - height});
    }

    render() {
        const {position} = this.state;
        const {image_X} = this.state;
        const {image_Y} = this.state;
        const {City} = this.state;
        const {Tide} = this.state;
        var Pos = position;
        var ImX = image_X;
        var ImY = image_Y;
        return (
            <div>
                <input type="file" ref="file" onChange={this.uploadFile} />
                <input type="file" ref="file" onChange={this.uploadFileTide} />
                <button onClick={this.AddImage}>click Here</button>
                <div className="layout" style={{ height: 850, width: 1850 onClick={this.testClick}>
                    <img src={Tide} alt="Tide" id="TideImg" className="Tide" style={{left:ImX, top:ImY}}  />
                </div>
            </div>
        );
    }
  }

testClickというクリックイベントを定義し、
testClick関数として、

    testClick = (e) =>{
        var element = document.getElementById( "TideImg" );
        var width = element.naturalWidth;
        var height = element.naturalHeight;
        this.setState({image_X:e.screenX });
        this.setState({image_Y:e.screenY - height});
    }

というように、
e.screenX、e.screenYでクリックした位置を取得、そちらに画像を移動させる処理を実行している形になります!!

■最後に

マウスでの画像操作は結構有用な気がするので、必要に応じて利用していこうと思います!!
後、ソースコードの整理も合わせて進めていきます!!