20190405の新人プログラマ応援に関する記事は4件です。

2019-02-06 新人応援 > 自分の背景知識にあった資料を参照しよう > 誰か他の人が薦めるものでなく

作ったまま放置していた記事。
公開。

新人の皆さま

色々な人が色々なことを言います。

  • 公式サイト何故見ないの?
    • 実情: 実は見ているが、読みにくい場合が。。。
    • 実情: 今読んでもさっぱり。代わりにブログの方が分かりやすい
      • ある程度、勉強してから読むと良い場合も
  • この本は素晴らしい、何故読まないの?
    • 実情: 今の自分の知識と合わない
    • 実情: その分野にはたいして興味はない
  • この言語やっておけば大丈夫
    • 実情: その人と自分はやっていることが違うので大丈夫でもない

大切なこと

人生で最も大切な出会いは自分自身と出会うことなのだと

イブ・サンローラン

ドキュメンタリー映画(監督: ピエール・トレットン)より

自分に合ったものを見つけよう

様々な背景を持つ様々な人たち
同じ背景、同じ成長過程、同じ成長速度の人はいない

人のアドバイスは「考える糧」としておくのは大切。
それに盲信するのではなく、良いとこどり、しよう。

おやじは、僕が理解できないようなところまで、無理に教えようとしたことは一度もないんだ。僕の準備ができていなければ、知識にならないからね。

from
アップルを創った怪物 (Computer Geek to Cult Icon) by スティーブ・ウォズニアック 2011-06-29

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

後輩にリーダブルコードを読ませてわかったギャップ

※こちらの記事はリーダブルコードを読んだ感想ではありません

最近、新人さんに教育をする場面が多くなり、彼らのコードを見てコードの書き方、お作法的なことが足りてないなと感じました。(読みにくい・・・(^_^)

そこでリーダブルコードを読んでもらおうと思いました。
本は自宅にあるのでそれを持ってこようかとも思いましたが、上司に相談したところ会社のお金で買っていいという話になりました。
2冊買いました。

サンキュー!上司!経費だぜ!

ちなみにリーダブルコードはプログラミングの『可読性』について書かれた本で言語にかかわらず参考になると思います。
技術書の中でもトップレベルに売れている本です。
母校の大学図書館にもありました!
今回の機会に私も読み直しましたが、いい本だと改めて思いました。
読んでない方はぜひ読んでください。(布教活動するオタクの図)
otaku.png

後輩たちの感想

何人かの後輩たちに読んでもらって感想を聞きました。
対象は1~2年目の社員です。

感想

・悪い例といい例が載っていてわかりやすかった
・悪い例の書き方をしていたので今後は直していきたい
・使用言語の経験がないのでわからない部分があった
・最後の方(テストコードの話)は書いたことがないのでイメージが掴めなかった

また上司から他におすすめの本があったら買っていいよと言われていたので、2年目の社員を中心に他にどんな本があったらいいかと聞いてみました。
会社にある本は入門系か辞書的なものしかなく、このようなテクニックについて記載された本はなかったためです。

要望

問題集のようなものがほしいという意見が多かったです。
上記にある通り、弊社に入門書はあります。
ただ入門書を一通り読んだあと何を書けばいいかわからないというようです。

話を聞いて感じたギャップ

cat2_1_idea.png

1つ目

感想の下2つにあるやったことが無いのでわからなかった系の感想
これは良くないなと思いました。

特に今回は読みやすさを題材にした本だったので処理の内容はともかく目的などは解説されていたと思います。
また目的まで理解できなかったのであればそれについて調べたり考えたりする必要はあるのかなと思います。

わからないことをわからないままにしない
実体験がなくても意味や目的を想像する

ということができていないように感じました

2つ目

要望にある問題集がほしいという意見
これは多かったのですが良くないと思いました

例えば『1~10までの合計を出力しろ』という問題があり、ステップなど考えればほぼほぼ1つの正解のようなものは出来上がるかもしれません

ただそれに意味はあるのでしょうか?
その問題集にあった処理を業務でそのまま組み込むことはあるのでしょうか?
そもそも問題集的な本を見かけませんがそれはなぜでしょう?

paizaなどプログラミングの問題を出してくれるサイトは知っていますが、あれは力試しであって練習問題ではないと思っています。

じゃあプログラム練習はどうすればよいか?という話

これをいうと後輩たちには嫌な顔をされるのですが、なにか作ってみるのが一番いいと思います。
何を作ればいいかどうすればいいのかわからないと言われます。

言語を習得しようと思うたびに掲示板や電卓などを作っていました。
プログラミングスキルを向上させようという目的なのであれば設計は使い回しでいいと思っています。
なので毎回同じ設計、画面のHTMLとか同じでいいのあればそのまま使っていました

仮想サーバ用意して画面書いてDB作って・・・
自分がやりたいのはサーバーで処理してる言語の勉強なのになかなかたどり着かない・・・
と最初は思いますが経験になっていると思います。

ただモノを作るのに目的がないとモチベーションが続かないというのはわかります。
私は当時githubなどの存在を知らなかったので↑で作ったコードは当時使っていたPCとともに消えてなくなりました(笑)
転職のときのポートフォリオとして使えないと思うのでいいです!
今のようなIT系の仕事をしていたわけでもないので見てもらうような相手もいませんでした

みなさんはgithubに公開して世界中の人に見てもらってもいいし、会社の先輩に確認して貰えばいいのではないかと思います。
私は見てって言われたら喜んでみます。

おしまい

happy_schoolboy.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

後輩にリーダブルコードを読ませてわかったこと

※こちらの記事はリーダブルコードを読んだ感想ではありません

最近、新人さんに教育をする場面が多くなり、彼らのコードを見てコードの書き方、お作法的なことが足りてないなと感じました。(読みにくい・・・(^_^)

そこでリーダブルコードを読んでもらおうと思いました。
本は自宅にあるのでそれを持ってこようかとも思いましたが、上司に相談したところ会社のお金で買っていいという話になりました。
2冊買いました。

サンキュー!上司!経費だぜ!

ちなみにリーダブルコードはプログラミングの『可読性』について書かれた本で言語にかかわらず参考になると思います。
技術書の中でもトップレベルに売れている本です。
母校の大学図書館にもありました!
今回の機会に私も読み直しましたが、いい本だと改めて思いました。
読んでない方はぜひ読んでください。(布教活動するオタクの図)
otaku.png

後輩たちの感想

何人かの後輩たちに読んでもらって感想を聞きました。
対象は1~2年目の社員です。

感想

・悪い例といい例が載っていてわかりやすかった
・悪い例の書き方をしていたので今後は直していきたい
・使用言語の経験がないのでわからない部分があった
・最後の方(テストコードの話)は書いたことがないのでイメージが掴めなかった

また上司から他におすすめの本があったら買っていいよと言われていたので、2年目の社員を中心に他にどんな本があったらいいかと聞いてみました。
会社にある本は入門系か辞書的なものしかなく、このようなテクニックについて記載された本はなかったためです。

要望

問題集のようなものがほしいという意見が多かったです。
上記にある通り、弊社に入門書はあります。
ただ入門書を一通り読んだあと何を書けばいいかわからないというようです。

話を聞いて感じたギャップ

cat2_1_idea.png

1つ目

感想の下2つにあるやったことが無いのでわからなかった系の感想
これは良くないなと思いました。

特に今回は読みやすさを題材にした本だったので処理の内容はともかく目的などは解説されていたと思います。
また目的まで理解できなかったのであればそれについて調べたり考えたりする必要はあるのかなと思います。

わからないことをわからないままにしない
実体験がなくても意味や目的を想像する

ということができていないように感じました

2つ目

要望にある問題集がほしいという意見
これは多かったのですが良くないと思いました

例えば『1~10までの合計を出力しろ』という問題があり、ステップなど考えればほぼほぼ1つの正解のようなものは出来上がるかもしれません

ただそれに意味はあるのでしょうか?
その問題集にあった処理を業務でそのまま組み込むことはあるのでしょうか?
そもそも問題集的な本を見かけませんがそれはなぜでしょう?

paizaなどプログラミングの問題を出してくれるサイトは知っていますが、あれは力試しであって練習問題ではないと思っています

20190406訂正

頂いたコメントから上記内容を訂正します

例えば『1~10までの合計を出力しろ』という問題があり

これらの問題を解くこと意味がないとは思いません
むしろエンジニアにとっては写経といって他人の書いたコードを写す(コピペはしない)でもそれなりに意味があります
さらにそのコードを上位者の人にレビューしてもらう環境があるのであればなお良いです
コードレビューはする側、される側にとてもいい効果を生むと思います

paizaやAtCoderなどで力試しをするのもいいと思います
ただこのようなマイクロ課題をたくさん解いてもある一定以上はコーディング力は上がらないと思います

スライムを10000体倒してもレベルが上がらなくなってしまう現象と同じです(違うかもしれない)
スライムを倒して効率よくレベルが上げられる時期は上記のような課題をこなせば良いと思います
でもその時期ってとても短いんですよね・・・

真面目にお勉強を頑張れば数ヶ月もせずそのレベルは脱してしまうと思いますし、弊社の後輩さんたちは1年近くの経験を積んでいるのでそこは超えていてほしいという思いが文章に出てしまったかもしれません

じゃあプログラム練習はどうすればよいか?という話

これをいうと後輩たちには嫌な顔をされるのですが、なにか作ってみるのが一番いいと思います。
何を作ればいいかどうすればいいのかわからないと言われます。

言語を習得しようと思うたびに掲示板や電卓などを作っていました。
プログラミングスキルを向上させようという目的なのであれば設計は使い回しでいいと思っています。
なので毎回同じ設計、画面のHTMLとか同じでいいのあればそのまま使っていました

仮想サーバ用意して画面書いてDB作って・・・
自分がやりたいのはサーバーで処理してる言語の勉強なのになかなかたどり着かない・・・
と最初は思いますが経験になっていると思います。

ただモノを作るのに目的がないとモチベーションが続かないというのはわかります。
私は当時githubなどの存在を知らなかったので↑で作ったコードは当時使っていたPCとともに消えてなくなりました(笑)
転職のときのポートフォリオとして使えないと思うのでいいです!
今のようなIT系の仕事をしていたわけでもないので見てもらうような相手もいませんでした

みなさんはgithubに公開して世界中の人に見てもらってもいいし、会社の先輩に確認して貰えばいいのではないかと思います
私は見てって言われたら喜んでみます!

まとめ

リーダブルコードに限らず技術書は常に書いてあるとおりすればいいというものではないと思います
そのため書いてある内容だけでなく目的やそれにかかるコストを考えていかないといけないと思います

技術書はプロジェクトで発生した問題の答えが載っている本ではないので、常にこの知識はどういう場面で役立つのか考えながら読んで貰えればいいなと思いました

happy_schoolboy.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go言語で作るマリオ風2Dゲーム

概要

Go言語で2Dのゲームアプリの作り方を調べたので、簡単なゲームをサンプルとして作りました。
こちらにソースコード一式があります。

作成したもの

以下の画像のように、マリオのようなゲームを作りました。
output.gif

緑の玉がプレーヤーで、青いお化けが敵です。
ステージ上にランダムに配置された落とし穴に落ちたり、ふわふわ動くお化けに当たったりしたら、ゲームオーバーです。

ゲームのステージは2種類あり、上のものの他にも、雪のステージもあります。
snow.gif

使用するライブラリについて

engoというライブラリを用いることで、クロスプラットフォームなデスクトップゲームアプリができます。
このライブラリを使用する上で必要となる基本的な概念を、以下で説明します。

ライブラリの基本的な概念

Entityとは

スクリーンに描画をされて、毎フレームごとに移動や当たり判定などの何らかの処理を行いたいものがある場合は、それらをEntityとして宣言をする必要があります。

私が作成したゲームだと、緑のプレーヤー青いお化けの敵、そして地面や草や木の3種類のエンティティをEntityとして登録しています。

Entityとして登録するには、以下のフィールドを保持する構造体を作ります。

type Sample struct {
    ecs.BasicEntity
    common.RenderComponent
    common.SpaceComponent
}

RenderComponentではEntityの見た目に関する情報を、SpaceComponentでは位置に関する情報を保持します。

Systemとは

上で説明したEntityを、Systemに登録をすることで、画面上に描画処理をしたり毎フレームごとになんらかの処理を行ったりできるようになります。

Systemを宣言するには、以下のフィールドを保持する構造体を作ります。

type SampleSystem struct {
    texture *common.Texture
    sampleEntity *Sample
    world *ecs.World
}

textureは見た目を定義するものであり、sampleEntityは上で説明したEntityを保持するものです。

そして、作成した構造体に以下の3つのメソッドを持たせます。

func (*SampleSystem) New(w *ecs.World){}
func (*SampleSystem) Remove(ecs.BasicEntity) {}
func (*SampleSystem) Update(dt float32) {}

New()Systemが作成された時に、Remove()は削除された時に、Update()は毎フレームに、それぞれ呼び出されるので、必要な処理を中に記述します。

通常New()では見た目の設定など初期設定を、Update()では移動や当たり判定などの処理を、それぞれ行います。

ゲームの作成

詳細なソースコードはGitHubにありますが、ここでは一部をかいつまんで説明します。

背景の作成

地面を描画します。
素材はここからとってきます。
tilesheet_grass.png
tilesheet_snow.png

この素材の一部をタイルのように画面に張り付けていきます。まずはEntitySystemの宣言です。

tileSystem.go
// Entity
type Tile struct {
    ecs.BasicEntity
    common.RenderComponent
    common.SpaceComponent
}

// System
type TileSystem struct {
    world *ecs.World
    // x軸座標
    positionX int
    // y軸座標
    positionY int
    tileEntity []*Tile
    texture *common.Texture
}

続いて、New()関数でこれらを描画をしていきます。

クリックしてコードを展開
tileSystems.go
func (ts *TileSystem) New(w *ecs.World){
    rand.Seed(time.Now().UnixNano())

    ts.world = w
    // 落とし穴作成中の状態を保持(0 => 作成していない、1以上 => 作成中)
    tileMakingState := 0
    // 雲の作成中の状態を保持 (0の場合:作成していない、奇数の場合:{(x+1)/2}番目の雲の前半を作成中、偶数の場合:{x/2}番目の雲の後半を作成中)
    cloudMakingState := 0
    // 雲の高さを保持
    cloudHeight := 0
    // タイルの作成
    tmp := rand.Intn(2)
    var loadTxt string
    // ランダムにステージを選ぶ
    if tmp == 0 {
        loadTxt = "tilemap/tilesheet_grass.png"
    } else {
        loadTxt = "tilemap/tilesheet_snow.png"
    }
    Spritesheet = common.NewSpritesheetWithBorderFromFile(loadTxt, 16, 16, 0, 0)
    Tiles := make([]*Tile, 0)
    for j := 0; j < 2800; j++ {
        // 地表の作成
        if (j > 10){
            if (tileMakingState > 1 && tileMakingState < 4){
                for t:= 0; t < 8; t++ {
                    FallPoint = append(FallPoint,j * 16 - t)
                }
            } else if (tileMakingState == 0){
                // すでに作成中でない場合、たまに落とし穴を作る
                randomNum := rand.Intn(10)
                if (randomNum == 0) {
                    FallStartPoint = append(FallStartPoint,j * 16)
                    tileMakingState = 1
                }
            }
        }
        // 描画するタイルを保持
        var selectedTile int
        // 描画するタイルを選択
        switch tileMakingState {
            case 0: selectedTile = 1
            case 1: selectedTile = 2
            case 2: tileMakingState += 1; continue
            case 3: tileMakingState += 1; continue
            case 4: selectedTile = 0
        }
        // タイルEntityの作成
        tile := &Tile{BasicEntity: ecs.NewBasic()}
        // 位置情報の設定
        tile.SpaceComponent.Position = engo.Point{
            X: float32(j * 16),
            Y: float32(237),
        }
        // 見た目の設定
        tile.RenderComponent.Drawable = Spritesheet.Cell(selectedTile)
        tile.RenderComponent.SetZIndex(0)
        Tiles = append(Tiles, tile)

        if (tileMakingState > 0){
            if (tileMakingState == 4){
                tileMakingState = 0
                continue
            }
            tileMakingState += 1
        }
    }
    for j := 0; j < 2800; j++ {
        // 雲の作成
        if (cloudMakingState == 0){
            randomNum := rand.Intn(6)
            if (randomNum < 7 && randomNum % 2 == 1) {
                cloudMakingState = randomNum
            }
            cloudHeight = rand.Intn(70) + 10
        }
        if (cloudMakingState != 0){
            // 雲Entityの作成
            cloudTile := cloudMakingState + 9
            cloud := &Tile{BasicEntity: ecs.NewBasic()}
            cloud.SpaceComponent.Position = engo.Point{
                X: float32(j * 16),
                Y: float32(cloudHeight),
            }
            cloud.RenderComponent.Drawable = Spritesheet.Cell(cloudTile)
            cloud.RenderComponent.SetZIndex(0)
            Tiles = append(Tiles, cloud)
            // 前半を作成中であれば、次は後半を作成する
            if (cloudMakingState % 2 == 1){
                cloudMakingState += 1
            } else {
                cloudMakingState = 0
            }
        }
        //草の作成
        if (!utils.Contains(FallPoint,j * 16)){
            // 落とし穴の上には作らない
            var grassTile int
            randomNum := rand.Intn(18)
            if (randomNum  < 6) {
                grassTile = 26 + randomNum
                grass := &Tile{BasicEntity: ecs.NewBasic()}
                grass.SpaceComponent.Position = engo.Point{
                    X: float32(j * 16),
                    Y: float32(221),
                }
                grass.RenderComponent.Drawable = Spritesheet.Cell(grassTile)
                grass.RenderComponent.SetZIndex(1)
                Tiles = append(Tiles, grass)

            }
        }

    }
    // 地面の描画
    for i := 0; i < 3; i++ {
        tileMakingState = 0
        for j := 0; j < 2800; j++ {
            if (tileMakingState == 0){
                // 落とし穴を作る場合
                if (utils.Contains(FallStartPoint,j * 16)){
                    tileMakingState = 1
                }
            }
            // 描画するタイルを保持
            var selectedTile int
            // 描画するタイルを選択
            switch tileMakingState {
                case 0: selectedTile = 17
                case 1: selectedTile = 18
                case 2: tileMakingState += 1; continue
                case 3: tileMakingState += 1; continue
                case 4: selectedTile = 16
            }
            tile := &Tile{BasicEntity: ecs.NewBasic()}
            tile.SpaceComponent.Position = engo.Point{
                X: float32(j * 16),
                Y: float32(285 - i * 16),
            }
            tile.RenderComponent.Drawable = Spritesheet.Cell(selectedTile)
            tile.RenderComponent.SetZIndex(0)
            Tiles = append(Tiles, tile)

            if (tileMakingState > 0){
                if (tileMakingState == 4){
                    tileMakingState = 0
                    continue
                }
                tileMakingState += 1
            }
        }
    }
    tileMakingState = 0
    for _, system := range ts.world.Systems() {
        switch sys := system.(type) {
        case *common.RenderSystem:
            for _, v := range Tiles {
                ts.tileEntity = append(ts.tileEntity, v)
                sys.Add(&v.BasicEntity, &v.RenderComponent, &v.SpaceComponent)
            }
        }
    }
}

乱数を発生させて、ランダムで落とし穴や草、雲を作成しています。

敵の作成

敵のお化けを作ります。
お化けの画像はこちらからとってきました。
pipo-halloweenchara2016_19.png

まずはEntityとSystemを宣言します。

enemySystem.go
type Enemy struct {
    ecs.BasicEntity
    common.RenderComponent
    common.SpaceComponent
    // ジャンプの状態(0 => 着地中, 1 => 1ジャンプ中, 2 => 降下中)
    jumpState int
    // ジャンプの残り時間
    jumpDuration int
    // 移動の速度(0 ~ 2, 数値が高いほど早い)
    velocity int
    // 画面から消えているか
    ifDissappearing bool
}

type EnemySystem struct {
    world *ecs.World
    enemyEntity []*Enemy
    texture *common.Texture
}

続いて、New()関数で描画と配置を行います。

クリックしてコードを展開
enemySystem.go
func (es *EnemySystem) New(w *ecs.World){
    es.world = w
    Enemies := make([]*Enemy, 0)
    // ランダムで配置
    for i := 0; i < 44800; i++ {
        randomNum := rand.Intn(400)
        if (randomNum == 0){
            // 敵の作成
            enemy := Enemy{BasicEntity: ecs.NewBasic()}
            enemy.SpaceComponent = common.SpaceComponent{
                Position: engo.Point{X:float32(i),Y:float32(212)},
                Width: 30,
                Height: 30,
            }
            // 画像の読み込み
            texture, err := common.LoadedSprite("pics/ghost.png")
            if err != nil {
                fmt.Println("Unable to load texture: " + err.Error())
            }
            enemy.RenderComponent = common.RenderComponent{
                Drawable: texture,
                Scale: engo.Point{X:1.1, Y:1.1},
            }
            enemy.RenderComponent.SetZIndex(1)
            es.texture = texture
            for _, system := range es.world.Systems() {
                switch sys := system.(type) {
                case *common.RenderSystem:
                    sys.Add(&enemy.BasicEntity, &enemy.RenderComponent, &enemy.SpaceComponent)
                }
            }
            enemy.velocity = rand.Intn(3)
            Enemies = append(Enemies,&enemy)
        }
        es.enemyEntity = Enemies
    }
}

乱数を発生させて、ステージ上のランダムな位置にお化けを発生させます。

そしてUpdate()関数で、作成されたお化けを移動させます。

クリックしてコードを展開
enemySystem.go
func (es *EnemySystem) Update(dt float32) {
    // カメラとプレーヤーの位置を取得
    var cameraPosition float32
    var playerPositionX float32
    for _, system := range es.world.Systems() {
        switch sys := system.(type) {
        case *common.CameraSystem:
            cameraPosition = sys.X()
        case *PlayerSystem:
            playerPositionX = sys.playerEntity.SpaceComponent.Position.X
        }
    }
    for _, o := range es.enemyEntity{
        // 画面に描画されていないオブジェクトは移動処理をしない
        if (o.SpaceComponent.Position.X > cameraPosition - 240 && o.SpaceComponent.Position.X < cameraPosition + 200 && !o.ifDissappearing){
            // プレーヤーとの当たり判定
            if (o.SpaceComponent.Position.X == playerPositionX) {
                for _, system := range es.world.Systems() {
                    switch sys := system.(type) {
                    case *PlayerSystem:
                        sys.playerEntity.damage += 1
                    }
                }
            }
            o.SpaceComponent.Position.X -= float32(o.velocity + 1)
            // ジャンプをしていない場合
            if (o.jumpState == 0){
                o.jumpState = rand.Intn(2) + 1
                jumpTemp := rand.Intn(3)
                switch (jumpTemp) {
                    case 0: o.jumpDuration = 15
                    case 1: o.jumpDuration = 25
                    case 2: o.jumpDuration = 35
                }
            }
            // ジャンプ処理
            if (o.jumpState == 1){
                // ジャンプをし終わっていない場合
                if (o.jumpDuration > 0){
                    o.SpaceComponent.Position.Y -= 3
                    o.jumpDuration -= 1
                } else {
                    // ジャンプをし終わった場合
                    o.jumpState = 2
                }
            } else {
                // 降下をし終わっていない場合
                if (o.SpaceComponent.Position.Y < 212){
                    o.SpaceComponent.Position.Y += 3
                } else {
                    // 降下し終わった場合
                    o.jumpState = 0
                }
            }
        }else if (o.ifDissappearing){
            o.SpaceComponent.Position.Y += 3
        }
    }
}

ランダムな高さのジャンプを繰り返しながら、ランダムな速度で移動をさせます。

上にCameraSystemと出てきますが、これはゲーム内の視点を動かすために、ライブラリで最初から用意されているSystemです。

プレーヤーの作成

プレーヤーのEntitySystemを宣言します。

playerSystem.go
type Player struct {
    ecs.BasicEntity
    common.RenderComponent
    common.SpaceComponent
    // ジャンプの時間
    jumpDuration int
    // カメラの進んだ距離
    distance int
    // 落ちているかどうか
    ifFalling bool
    // ダメージ
    damage int
}

type PlayerSystem struct {
    world *ecs.World
    playerEntity *Player
    texture *common.Texture
}

New()関数で描画をします。

クリックしてコードを展開
playerSystem.go
func (ps *PlayerSystem) New(w *ecs.World){
    ps.world = w
    // プレーヤーの作成
    player := Player{BasicEntity: ecs.NewBasic()}

    // 初期の配置
    positionX := int(engo.WindowWidth() / 2)
    positionY := int(engo.WindowHeight() - 88)
    player.SpaceComponent = common.SpaceComponent{
        Position: engo.Point{X:float32(positionX),Y:float32(positionY)},
        Width: 30,
        Height: 30,
    }
    // 画像の読み込み
    texture, err := common.LoadedSprite("pics/greenoctocat.png")
    if err != nil {
        fmt.Println("Unable to load texture: " + err.Error())
    }
    player.RenderComponent = common.RenderComponent{
        Drawable: texture,
        Scale: engo.Point{X:0.1, Y:0.1},
    }
    player.RenderComponent.SetZIndex(1)
    ps.playerEntity = &player
    ps.texture = texture
    for _, system := range ps.world.Systems() {
        switch sys := system.(type) {
        case *common.RenderSystem:
            sys.Add(&player.BasicEntity, &player.RenderComponent, &player.SpaceComponent)
        }
    }
    common.CameraBounds = engo.AABB{
        Min: engo.Point{X: 0, Y: 0},
        Max: engo.Point{X: 40000, Y: 300},
    }
}

Update()関数で、移動をします。

クリックしてコードを展開
playerSystem.go
func (ps *PlayerSystem) Update(dt float32) {
    // ダメージが1であればゲームを終了
    if ps.playerEntity.damage > 0 {
        whenDied(ps)
    }
    // 落とし穴
    if (ps.playerEntity.jumpDuration == 0 && utils.Contains(FallPoint,int(ps.playerEntity.SpaceComponent.Position.X)) ){
        ps.playerEntity.ifFalling = true
        ps.playerEntity.SpaceComponent.Position.Y += 5
    }
    // 穴に落ち切ったらライフを0にする
    if ps.playerEntity.SpaceComponent.Position.Y > 300 {
        ps.playerEntity.damage += 1
    }

    if(!ps.playerEntity.ifFalling){
    // 右移動
    if engo.Input.Button("MoveRight").Down()  { 
        // 画面の真ん中より左に位置していれば、カメラを移動せずプレーヤーを移動する
        if (int(ps.playerEntity.SpaceComponent.Position.X) < ps.playerEntity.distance + int(engo.WindowWidth()) / 2){
            ps.playerEntity.SpaceComponent.Position.X += 5
        } else {
            // 画面の右端に達していなければプレーヤーを移動する
            if (int(ps.playerEntity.SpaceComponent.Position.X) < ps.playerEntity.distance + int(engo.WindowWidth()) - 10){
                ps.playerEntity.SpaceComponent.Position.X += 5
            }
            // カメラを移動する
            engo.Mailbox.Dispatch(common.CameraMessage{
                Axis:        common.XAxis,
                Value:       5,
                Incremental: true,
            })
            ps.playerEntity.distance += 5
        }
    }
    // プレーヤーを左に移動
    if engo.Input.Button("MoveLeft").Down()  {
        if int(ps.playerEntity.SpaceComponent.Position.X) > ps.playerEntity.distance + 10{
            ps.playerEntity.SpaceComponent.Position.X -= 5
        }
    }
    // プレーヤーをジャンプ
    if engo.Input.Button("Jump").JustPressed() {
        if ps.playerEntity.jumpDuration == 0 {
            ps.playerEntity.jumpDuration = 1
        }
    }
    if ps.playerEntity.jumpDuration != 0 {
        ps.playerEntity.jumpDuration += 1
        if ps.playerEntity.jumpDuration < 14 {
            ps.playerEntity.SpaceComponent.Position.Y -= 5
        } else if ps.playerEntity.jumpDuration < 26 {
            ps.playerEntity.SpaceComponent.Position.Y += 5
        } else {
            ps.playerEntity.jumpDuration = 0
        }
    }
    }
}

移動をするだけでなく、落とし穴に落ちたらゲームオーバーにする、などの処理も行なっています。

ゲームの開始

上で作成したSystemなどを用いて、ゲームを動かします。

ゲームプログラムのメインの部分は、以下のようになります。

game.go
package main

import (
    "bytes"
    "engo.io/engo"
    "engo.io/engo/common"
    "engo.io/ecs"
    "image/color"
    "golang.org/x/image/font/gofont/gosmallcaps"
    "./systems"
)

type myScene struct {}

func (*myScene) Type() string { return "myGame" }

func (*myScene) Preload() {
    // 必要なファイルを事前に読み込んでおく
    engo.Files.Load("pics/greenoctocat.png", "pics/ghost.png", "tilemap/tilesheet_grass.png", "tilemap/tilesheet_snow.png")
    engo.Files.LoadReaderData("go.ttf", bytes.NewReader(gosmallcaps.TTF))
    common.SetBackground(color.RGBA{255, 250, 220, 0})
}

func (*myScene) Setup(u engo.Updater){
    engo.Input.RegisterButton("MoveRight", engo.KeyD, engo.KeyArrowRight)
    engo.Input.RegisterButton("MoveLeft", engo.KeyA, engo.KeyArrowLeft)
    engo.Input.RegisterButton("Jump", engo.KeySpace)
    world, _ := u.(*ecs.World)
    // Systemの追加
    world.AddSystem(&common.RenderSystem{})
    world.AddSystem(&systems.TileSystem{})
    world.AddSystem(&systems.PlayerSystem{})
    world.AddSystem(&systems.EnemySystem{})
}

func (*myScene) Exit() {
    engo.Exit()
}

func main(){
    opts := engo.RunOptions{
        Title:"myGame",
        Width:400,
        Height:300,
        StandardInputs: true,
        NotResizable:true,
    }
    engo.Run(opts,&myScene{})
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む