年の功より亀の甲

カメがプログラミングとか技術系について書くブログです。

関西ぶらり旅 予告記事

こんにちは、コロナです。

先月自主的に遠征していた「関西ぶらり旅 ~勉強会講演~」の予告記事になっています

スケジュールとしては、以下のような旅日程でした。

日付 イベント名 URL 概要 公開URL
2020-01-23 09:40~13:00
飛行機
千歳から大阪へ 特になし

KansaiTechReportFirstDay1.zip - Google ドライブ

2020-01-23 19:00~21:00
vscode勉強会
VS Code Meetup #2 - Live Share編(大阪会場) - connpass

KansaiTechReportFirstDaySecond_v0.2.zip - Google ドライブ

2020-01-24 09:30~17:30
Microsoft Ignite The Tour
Microsoft Ignite The Tour Osaka - 2020年1月

KansaiTechReportSecondDay_v0.1.zip - Google ドライブ

2020-01-25 12:00~18:00
新大阪UE4勉強会 #1
新大阪UE4勉強会 #1 - connpass 勉強会~懇親会のお話からの学習まで 全部で3分程度になる予定
2020-01-26 14:00~19:00
Kobe HoloLens Meetup! vol.2 ー HoloLens 2 関西上陸記念勉強会
Kobe HoloLens Meetup! vol.2 ー HoloLens 2 関西上陸記念勉強会 - connpass hololensのセッション話
2020-01-27 15:30~19:30
飛行機
神戸から千歳へ 帰宅


帰った日以外、全て勉強会を入れていたので記事は個別に出していきます
*予定では、来週末に各イベントの感想記事は出す予定なのでお楽しみにしてください
個別記事予定日
2020/02/14~2020/02/15 で適宜
レポートに出す、大阪の美少女キャラクターを鋭意考え中です

Global Game Jam 2020に参加してきました - 番外編 -

こんにちは、カメドット ( @kamedot ) です。

今回の記事ではGlobalGameJJamの番外編になります。
内容としては、技術的に作業した内容に、特化した「こんな作業していたよ('◇')ゞ」 という記事です。

各シーン毎に紹介をしていきます

タイトルシーン

インスペクター

gyazo.com

SceneManagerのスニペット

TitleScene...タイトル画面内の操作や効果音の組み込み
AudioTestManager...各種サウンドを鳴らすための処理を保持

gyazo.com
gyazo.com

Title.cs全体

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>
/// タイトルシーン用のスクリプト
/// </summary>
public class TitleScene : MonoBehaviour
{

    /// <summary>
    /// 音楽マネージャクラス
    /// </summary>
    public AudioTestManager audioTestManager;
    



    /// <summary>
    /// 開始時処理
    /// </summary>
    void Start()
    {
        
    }


    /// <summary>
    /// ゲームループ内の呼び出し処理
    /// </summary>
    void Update()
    {
        if (Input.GetKey(KeyCode.Joystick1Button0)|| Input.GetKey(KeyCode.Joystick2Button0))
        {
            audioTestManager.PlayDecide();
            SceneManager.LoadScene("Main");
        }

        if (Input.GetKey(KeyCode.Joystick1Button2) || Input.GetKey(KeyCode.Joystick2Button2))
        {
            audioTestManager.PlayDecide();
            SceneManager.LoadScene("Description");
        }


    }
}

以下は、実装した内容の説明

audioTestManager.PlayDecide()で決定音を鳴らして、
SceneManager.LoadScene("Main"); でゲーム開始用画面へ遷移しています

        if (Input.GetKey(KeyCode.Joystick1Button0)|| Input.GetKey(KeyCode.Joystick2Button0))
        {
            audioTestManager.PlayDecide();
            SceneManager.LoadScene("Main");
       }

audioTestManager.PlayDecide()で決定音を鳴らして、
SceneManager.LoadScene("Description"); で操作説明用画面へ遷移しています

        if (Input.GetKey(KeyCode.Joystick1Button2) || Input.GetKey(KeyCode.Joystick2Button2))
        {
            audioTestManager.PlayDecide();
            SceneManager.LoadScene("Description");
        }


操作説明画面

インスペクター
DescriptionSceneに、操作説明用として以下のパラメータを用意
Images...操作説明画面で使うスクリーンショットのリスト
Texts........操作説明画面の画像とセットの説明文章画像

gyazo.com

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>
/// 操作説明画面のシーンスクリプト
/// </summary>
public class DescriptionScene : MonoBehaviour
{

    public AudioTestManager audioTestManager;

    /// <summary>
    /// 現在選択中のページ番号
    /// </summary>
    public int PageNumber = 0;

    /// <summary>
    /// 実際の表示で利用している画像オブジェクト
    /// </summary>
    public GameObject image;

    /// <summary>
    /// 操作説明画面内で使うスクリーンショットの配列
    /// </summary>
    public GameObject[] images;

    /// <summary>
    /// 操作説明画面内で使うスクリーンショットの説明画像配列
    /// </summary>
    public GameObject[] texts;


    // Start is called before the first frame update
    void Start()
    {
        
    }


    void Update()
    {
        if (Input.GetKey(KeyCode.Joystick1Button0) && audioTestManager != null)
        {
            audioTestManager.PlayDecide();
            SceneManager.LoadScene("Title");
        }

        // Xボタン次の画面へ遷移する Button 1
        if (Input.GetKeyDown(KeyCode.Joystick1Button1)) {
            if (PageNumber < images.Length && audioTestManager != null)
            {
                audioTestManager.PlayDecide();
                PageNumber++;
            }
        }


        // Bボタン 前の画面へ遷移する Button 2
        if (Input.GetKeyDown(KeyCode.Joystick1Button2))
        {
            if (PageNumber > 0 && audioTestManager != null) {
                
                audioTestManager.PlayDecide();
                PageNumber--;
            }
        }


        // 表示変更
        for (int i = 0; i < images.Length; i++) {
            if (i == PageNumber) {
               image.GetComponent<SpriteRenderer>().sprite = images[i].GetComponent<SpriteRenderer>().sprite;
            }
        }


    }
}

条件式で、現在選択中の説明文判定して、一致している場合はimagesのspriteを入れ替えています。

 if (i == PageNumber) {
               image.GetComponent<SpriteRenderer>().sprite = images[i].GetComponent<SpriteRenderer>().sprite;
  }

塔オブジェクトは、3dのパーツ組み立てをしていました。
*コードについては、バトルロジックを書いている方と重複しているので省略いたします
gyazo.com

プレファブ
操作説明画面やリザルトダイアログなどプログラム上から呼ぶことが前提のオブジェクトについて
prefabsへ投入したり作業を進めていました

赤い四角の枠で囲ったオブジェクトが今回作業をさせていただいたものになります。

f:id:sakuriver:20200208082323p:plain


以上、UIとモデリングを中心にプログラムの報告でしたー

参考記事
(はてなブログの @baba_s さんの記事)
(Unityの基本ページ Unity - マニュアル: Unity マニュアル )

Global Game Jam 2020に参加してきました - Part3 -

最終日の作業内容
・アップロードルールやディレクトリの確認
discordに確認した内容で連絡をしていました

https://i.gyazo.com/da18507d016ea881df0d19b45ea30169.mp4



・プロデューサーさんのスミスさんからお話をもらったテキストチャットのやり取り(これは、代筆)

・画像レイアウトのUnity化 Part2
gyazo.com

・その時点の最速でくめる仮操作のメモ残しと

gyazo.com


・最小限の組み込みで物足りないものについて、追加対応
gyazo.com


続編
操作説明画面で、触って見せる内容を共有するときに動画を作成

www.youtube.com

動画サンプル
www.youtube.com


そんなこんなで、つよつよな学生さんと一緒に作った作品がいかになります。
globalgamejam.org


反省点
クラスの細かい部分での実装がしょぼくなった( 一一)
3D系の知識が足りなかったので、UIメインだったので機会があれば次回はシステム部分を頑張りたい

最後に名前を出していいよという方のdiscordの表示名と担当パートの紹介です。
今回は、開発自体はサブが多かったのですがUnity製の3Dゲームが動くところまで体験させていただいてありがとうございました。

マサ吉さん 2Dの各種素材や、ラフレイアウトなどを担当
あろえさん 今回のインターフェース設計から3Dプログラムのメイン担当

Global Game Jam 2020に参加してきました - Part2 -

カメドットです。

今回は、第2弾です。

discordネタがいくつかあるので、興味がある方で札幌GGJにいらっしゃる方は以下のリンクから見れたりしちゃいます

discordapp.com



初日のお話

  • 開発環境のセットアップの話

3Dゲームなのでモデリングソフトとゲームエンジンのバージョン確認をしていました。

Unity 2019.3.06f
Blender 2.79

  • 見積もり的なお話

2020/02/06 差分が生まれる前提条件を加筆










一番重たいところのお話が合ったけど、初Unityと規模間的にサポート重視のほうが確実性高いので。
自分では難しいと相談して、黒子系をやっていました

加筆開始箇所(つよつよな学生さんに作っていただいた内容です)

重たいところの見積りのお話
・せっかくだからInterfaceを用意して、使いたい
・初日23時時点で、企画整理中

実際に実装されたコード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class Warrior : MonoBehaviour, ICharacter
{
    /// <summary>
    /// 戦士のステータスデータ
    /// </summary>
    private WarriorStatusData warriorStatusData = default;
    private WarriorStatus warriorStatus = default;
    /// <summary>
    /// 戦士の体力
    /// </summary>
    public int HitPoint = 100;
    /// <summary>
    /// 戦士の攻撃力
    /// </summary>
    public int AttackPower = 10;
    /// <summary>
    /// 戦士の防御力
    /// </summary>
    public int GuardPower = 10;
    /// <summary>
    /// 戦士のタイプ
    /// WarriorStatus参照
    /// 今のところ0=Attack,1=Deffence
    /// </summary>
    public int WarriorType = 0;
    /// <summary>
    /// どっちのチームか
    /// 1=1P,2=2P
    /// </summary>
    public int TeamID = 1;
    /// <summary>
    /// 攻撃中かどうか
    /// </summary>
    public bool IsAttack = false;
    /// <summary>
    /// 死んでいるかどうか
    /// </summary>
    public bool IsDeath = false;
    /// <summary>
    /// 攻撃の間隔
    /// </summary>
    public float AttackInterval = 1;
    /// <summary>
    /// ↑を計算するために必要な一次変数くん
    /// </summary>
    public float tempTime;
    /// <summary>
    /// ゲームが終わったかどうか
    /// </summary>
    public bool IsEnd = false;

    ///<summary>
    ///効果音
    /// </summary>
    public AudioClip attackSE;
    public AudioSource audioSource;

    /// <summary>
    /// 戦士のNavMesh
    /// </summary>
    public NavMeshAgent agent;
    /// <summary>
    /// 戦士のアニメーターコントローラー
    /// </summary>
    public Animator WarriorAnimator = default;

    /// <summary>
    /// 初期化処理
    /// </summary>
    void Start()
    {
        // 戦士のプロパティを取得
        warriorStatusData = Resources.Load<WarriorStatusData>("WarriorData");
        warriorStatus = warriorStatusData.WarriorStatusList[WarriorType];
        this.HitPoint = warriorStatusData.HP;

        // 1Pと2Pのマテリアルを変更
        this.GetComponentInChildren<SkinnedMeshRenderer>().material = warriorStatus.WarriorMaterials[TeamID - 1];
        agent = this.GetComponent<NavMeshAgent>();
        WarriorAnimator = this.GetComponent<Animator>();
    }

    /// <summary>
    /// 何かに衝突した時に起きる処理
    /// </summary>
    /// <param name="other"></param>
    private void OnTriggerStay(Collider other)
    {
        // 死んでたら何もしない
        if (this.IsDeath || this.IsEnd)
        {
            return;
        }
        // 相手の戦士に当たった時の処理
        if (other.tag == "Warrior")
        {
            if (other.GetComponent<Warrior>().IsDeath)
            {
                return;
            }
            if (other.GetComponent<Warrior>().TeamID != this.TeamID)
            {
                if (IsAttack)
                {
                    tempTime += Time.deltaTime;
                    if (tempTime >= AttackInterval)
                    {
                        Attack(other.gameObject);
                        tempTime = 0;
                    }
                    //StartCoroutine("Attack", other.gameObject);
                }
                else
                {
                    IsAttack = true;
                }
            }
        }
        // 敵の塔に当たった時の処理
        else if (other.tag == "Tower")
        {
            if (other.GetComponent<Tower>())
            {
                if (other.GetComponent<Tower>().TeamId != this.TeamID)
                {
                    if (IsAttack)
                    {
                        tempTime += Time.deltaTime;
                        if (tempTime >= AttackInterval)
                        {
                            Attack(other.gameObject);
                            tempTime = 0;
                        }
                        //StartCoroutine("Attack", other.gameObject);
                    }
                    else
                    {
                        IsAttack = true;
                    }
                }
            }
        }
    }

    // 敵の塔とか戦士から離れたときの処理
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Warrior")
        {
            if (other.GetComponent<Warrior>().TeamID != this.TeamID)
            {
                IsAttack = false;
                tempTime = 0;

            }
        }
        else if (other.tag == "Tower")
        {
        }
    }

    /// <summary>
    /// 戦士を動かす処理
    /// </summary>
    /// <param name="point">動かす座標</param>
    public void MoveWarrior(Vector3 point)
    {
        if (IsDeath || IsEnd)
        {
            return;
        }
        this.agent.SetDestination(point);
    }


    /// <summary>
    /// 敵戦士や塔を攻撃する処理
    /// </summary>
    public void Attack(GameObject target)
    {
        // 死んでたら何もしない
        if (IsDeath || IsEnd)
        {
            return;
        }
        //while (true)
        //{
        // 敵の方向見る
        //Quaternion targetRotation = Quaternion.LookRotation(target.transform.position - this.transform.position);
        //transform.rotation = Quaternion.Slerp(this.transform.rotation, targetRotation, Time.deltaTime);
        this.agent.SetDestination(this.transform.position);
        // 攻撃
        target.GetComponent<ICharacter>().Damage(warriorStatus.Atk);
        WarriorAnimator.SetTrigger("Attack");
        // 1秒待つ
        //yield return new WaitForSeconds(1);
        //}

    }

    /// <summary>
    /// 敵戦士からダメージ受ける処理
    /// </summary>
    /// <param name="damage">ダメージ量</param>
    public void Damage(int damage)
    {
        this.HitPoint -= damage * (100 - warriorStatus.Def) / 100;
        if (this.HitPoint <= 0)
        {
            Death();
        }
    }

    /// <summary>
    /// 賢者から回復受ける処理
    /// </summary>
    /// <param name="repair">回復量</param>
    public void Repair(int repair)
    {
        // 死んでたら回復されない
        if (IsDeath || IsEnd)
        {
            return;
        }
        this.HitPoint += repair;
        if (this.HitPoint >= 100)
        {
            HitPoint = 100;
            return;
        }
    }

    /// <summary>
    /// 死んだときの処理
    /// </summary>
    public void Death()
    {
        // 死んだ
        IsDeath = true;
        this.GetComponent<NavMeshAgent>().SetDestination(this.transform.position);
        //死亡アニメーション再生
        WarriorAnimator.SetBool("isDeath", true);
    }

    /// <summary>
    /// リスポーン処理
    /// </summary>
    public void Respawn()
    {
        IsDeath = false;
        // 復活アニメーション
        WarriorAnimator.SetBool("isDeath", false);
        // HPを半分回復
        this.HitPoint = 50;
        this.GetComponent<NavMeshAgent>().enabled = true;
    }

}

管理クラス

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class WarriorManager : MonoBehaviour
{
    /// <summary>
    /// 子オブジェクトにある戦士
    /// </summary>
    public List<GameObject> Warriors;
    /// <summary>
    /// 子オブジェクトにある戦士のNavMeshAgent
    /// </summary>
    public List<Warrior> WarriorScripts;

    // Start is called before the first frame update
    void Start()
    {
        // 子オブジェクトの戦士と,そのNavMeshAgentを取得
        foreach (Transform childObject in this.transform)
        {
            Warriors.Add(childObject.gameObject);
            WarriorScripts.Add(childObject.GetComponent<Warrior>());
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }


    /// <summary>
    /// 移動処理(賢者から呼び出し)
    /// </summary>
    /// <param name="point">戦士が移動する場所の座標</param>
    public void DecidePoint(Vector3 point)
    {
        foreach (Warrior warrior in WarriorScripts)
        {
            warrior.MoveWarrior(point);
        }
    }

    /// <summary>
    /// ゲームが終わった時の処理
    /// </summary>
    public void GameEnd()
    {
        foreach (Warrior warrior in WarriorScripts)
        {
            warrior.IsEnd = true;
        }
    }
}

ステータス管理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu]
public class WarriorStatusData : ScriptableObject
{
    public float AttackInterval = 1;
    public int HP = 100;
    //ListステータスのList
    public List<WarriorStatus> WarriorStatusList = new List<WarriorStatus>();
}

/// <summary>
/// 戦士のパラメータのクラス
/// </summary>
[System.Serializable]
public class WarriorStatus
{
    /// <summary>
    /// 攻撃型などの戦士のタイプ
    /// </summary>
    public string Name = "タイプ";
    /// <summary>
    /// 体力,攻撃力,防御力
    /// </summary>
    public int Atk = 15, Def = 15;
    public Material[] WarriorMaterials;
}


バトル関連

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameMaster : MonoBehaviour
{
    /// <summary>
    /// ゲームがスタートしたか否か
    /// </summary>
    public bool IsGameStart;
    /// <summary>
    /// ゲームが終わったか否か
    /// </summary>
    public bool IsGameEnd;
    /// <summary>
    /// 1P2Pの戦士の数
    /// </summary>
    public int WarriorNum1, WarriorNum2;
    /// <summary>
    /// 1P2Pの塔の残りの数
    /// </summary>
    public int TowerNum1, TowerNum2;
    /// <summary>
    /// 1P2Pの塔の残りHP
    /// </summary>
    public int TowerRestHP1, TowerRestHP2;
    /// <summary>
    /// 勝利したプレイヤー
    /// 0は引き分け
    /// </summary>
    public int Winner = 0;

    /// <summary>
    /// 1P2Pのwisemanインスタンス
    /// </summary>
    [SerializeField]
    private Wiseman wiseman1 = default, wiseman2 = default;

    [SerializeField]
    private EndText endText;
    [SerializeField]
    private Result result;

    // Start is called before the first frame update
    void Start()
    {
        // 一応動的に取得できるようにする
        wiseman1 = GameObject.FindGameObjectWithTag("Player1").GetComponent<Wiseman>();
        wiseman2 = GameObject.FindGameObjectWithTag("Player2").GetComponent<Wiseman>();
        // 最初は動けないように
        wiseman1.enabled = false;
        wiseman2.enabled = false;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    /// <summary>
    /// ゲームスタート処理
    /// </summary>
    public void GameStart()
    {
        IsGameStart = true;

        // プレイアブルにする
        wiseman1.enabled = true;
        wiseman2.enabled = true;
    }

    /// <summary>
    /// ゲームエンド処理
    /// </summary>
    public void GameEnd()
    {
        // 賢者の動きとめる
        wiseman1.enabled = false;
        wiseman2.enabled = false;

        // 戦士の動きとめる
        var warriorManagers = FindObjectsOfType<WarriorManager>();
        foreach (WarriorManager manager in warriorManagers)
        {
            manager.GameEnd();
        }

        // ゲーム終了時のテキスト出す
        endText.Show();

        CountWarrior();
        CheckTowerDamage();
        Winner = Judgement();
        StartCoroutine("ShowResult");
    }

    /// <summary>
    /// リザルト出す
    /// </summary>
    /// <returns></returns>
    IEnumerator ShowResult()
    {
        yield return new WaitForSeconds(2);
        result.ShowResult(WarriorNum1, WarriorNum2, TowerNum1, TowerNum2, Winner);

    }

    /// <summary>
    /// 戦士カウント処理
    /// </summary>
    public void CountWarrior()
    {
        var warriors = FindObjectsOfType<Warrior>();
        foreach (Warrior warrior in warriors)
        {
            // 死んでるか死んでないかで絞込
            if (!warrior.IsDeath)
            {
                // チームIDで絞込
                switch (warrior.TeamID)
                {
                    case 1:
                        WarriorNum1++;
                        break;
                    case 2:
                        WarriorNum2++;
                        break;
                    default:
                        break;
                }
            }
        }
    }

    /// <summary>
    /// 木のダメージカウント処理(塔の本数も数える)
    /// </summary>
    public void CheckTowerDamage()
    {
        var towers = FindObjectsOfType<Tower>();
        foreach (Tower tower in towers)
        {
            // 建っているかどうか
            if(tower.HitPoint > 0)
            {
                // チームIDで分岐
                switch (tower.TeamId)
                {
                    case 1:
                        TowerNum1++;
                        TowerRestHP1 += tower.HitPoint;
                        break;
                    case 2:
                        TowerNum2++;
                        TowerRestHP2 += tower.HitPoint;
                        break;
                    default:
                        break;
                }
            }
        }
    }

    /// <summary>
    /// どっちが勝ったかを判定
    /// 0だったら引き分け
    /// </summary>
    public int Judgement()
    {
        // 塔がより多く建っている方の勝ち
        if (TowerNum1 > TowerNum2)
        {
            return 1;
        }
        else if (TowerNum2 > TowerNum1)
        {
            return 2;
        }
        // 塔の体力が残っていたほうの勝ち
        else if (TowerRestHP1 > TowerRestHP2)
        {
            return 1;
        }
        else if (TowerRestHP2 > TowerRestHP1)
        {
            return 2;
        }
        else
        {
            return 0;
        }
    }
}

「今回企画で話し合った内容」では、アニメーション付きだったので完成後のアニメーション
gyazo.com

Editor上での再生例
https://i.gyazo.com/bb19c285b58141dd82e2250bdcd3b4fc.mp4

Warriorクラスが関係のあるバトル処理


「GGJでUnity3d初挑戦」では結構な物量があったので、断念

*断念しなくてもよくない?
→ここが人によって温度感があるところですが、個人的には「GGJであっても動くものになっていて欲しい」と「自分自身で怪しいものを持ち続けて最後にできませんでした」がつらいのは仕事上わかるので持っていくのをやめました。

*これ書かなくてもよくない?

  • よく「3Dと2Dってz軸座標ぐらいの違いだからいけるっしょ?」などのお話に波及してしまってまずかったため...

上記簡単に見えますが、商業で大規模の場合は人員が分かれているお仕事がなくなってしまうことを懸念したため

大きめだと、Unity後のマージだと以下の3人ぐらいでやったり等々があります。

職種 内容
基盤系エンジニア インターフェースクラスやクラス設計等
トルエンジニア バトル系ロジック
アニメータ 兵士のアニメータ調整

GGJや短期ハッカソンで、意欲旺盛でみんなでやろうぜが多いので上記のような分業はしないことがおおいのですが、
「おしごと」であれば分かれているので「おしごとを奪わない」ように書いてみました....
*スタートアップや立ち上げのふりーらすんの方は、保守よりも立ち上げを重視して、一人でやっている場合はまた別です

そのあとにやったタスク

BGMとサウンドについてアナログで一覧化

f:id:sakuriver:20200206022457j:plain

GGJでは、「遊べるところまで」にフォーカスしているのでメイン画面に注力していました

メインとフロー順に直すと、以下の音楽を最優先

  • ゲームメイン開始演出効果音
  • ゲームメインBGM
  • 命令発動の効果音
  • ゲームが時間切れになったときの効果音

次の音楽は最悪載らなかったら優先度を下げるようにしていました

  • タイトルBGM
  • 決定音(次の画面に進む場合やキャンセルするときに鳴らす用の音になります)


帰宅してからUnityの新バージョンを導入する

gyazo.com


githubの申請が届いていなかったので連絡をする

Blenderは逆にバージョンを下げて、インストールをして確認できたので共有をする

gyazo.com


コーディング規約のテキストファイルを見ながらシステムも合わせた認識合わせ
→継承関連について、ここで見逃す
 ・Unityのシーン命名規則
 ・Unityプロジェクトが自分のローカル環境で開いて確認できるか
 ・gitignoreが自分の環境で動かせなかったので相談→最終的にUnityCollabをあろえさんから提案していただいたので使う方向に
ライセンス関連
 ・素材ファイルのライセンス記入用ファイルを用意
課題管理
 ・デバッグのチケット範囲がGGJ的に大きいのでタイトルを変更(タイトル画面やオプション画面は優先度を下げて遊ぶ部分に集中)
情報共有
 ・デバッグ用のテストファイル上げ場所の相談→ドライブのURLを用意してもらったので、アートとサウンドをシートに一覧化
gyazo.com

gyazo.com

 *アートのお2人と命名規則を相談して、Unity化するのでプログラム側に合わせてもらう話
 ・グラフィックシートの改修
  *列の追加
   グラフィックシート
   素材ファイル→Unityへのインポート作業状態確認
 ・サウンド関連
  *世界観が並行作業だったので、組み込み用にメイン画面を中心にBGMとSEファイル仮用意
 ・プレファブ化
  ・塔オブジェクト
   *スクショを張り付ける
  ・結果ダイアログ  
   Unity上でレイアウトの仮組スクショを撮影して、アートメンバーとやり取り
   
gyazo.com

gyazo.com

 画面遷移のモックプログラム(タイトル→メイン、 タイトル→操作説明)
 決定音の仮組込
 画面レイアウトについて、組みあがっていなかったものを適宜相談
*最終日は、Part3へ続く

Global Game Jam 2020に参加してきました - Part1 -

こんにちは、 カメドットです。

先日行われた Global Game Jam に参加してきたので、振り返りの記事です

  • GlobalGameJamとは

「48時間以内でゲームを作ってアップロード」しようというゲーム開発イベントです
ふわっとしたテーマはあるので、作りたいネタを固めていないチームはテーマから考えたりします
日本だけではなくて、世界各国でイベントが実施されています

globalgamejam.org

  • 参加会場

今回は、札幌会場で参加をいたしました。
すすきののノルベサでした
globalgamejam.org

  • 初日

お仕事もあって途中参加で札幌会場に向かいました。

discordでやりとりするということだったので、申し込みをして参加しました。

discordapp.com


  • テーマの確認

時間に遅れてきたので、キーノートの動画を閲覧することから始めました。
今回は、動画が2段構成になっていました。

 - 開発の心構えやあんまり無理しないようにねというお話 
 - 「Repair」をテーマとして「こんなテーマで考えるといいよ」という動画も流されています

www.dropbox.com

  • パーティ探し

今回はdiscordに当日入った勢なので、拾ってもらうところから始めました
gyazo.com

いくつかお誘いがある中で、彗夢さん率いるUnityで3Dゲーム作ろうぜチームに入れていただきました
Unityの3DゲームはGGJでは初挑戦ジャンルなので、若干のどきどきでの開発参加になります


次回は、初日開発でお手伝いした内容や企画についてお話をしていきます

マイクリハッカソン -ディレクトリ構造編-

コロナです。
今回は、マイクリハッカソン報告の続きでディレクトリ構造編となります

ソースコードのURLはこちらになります

スマートコントラクトを実行するときですが、以下のようなディレクトリ構成で開発をしていました。

ディレクトリ構成

.firebase
firebaseのキャッシュファイルを格納
firebase.json
ログイン用のhtmlファイルのリダイレクト設定。
  ファイルの除外設定
app
css
default.css アプリ上で実際に使っているCSS
images PWA用の画像ファイルを格納しているディレクト
js
extension_asset_abi.js エクステンションのコントラクトアドレスと、abiファイル設定を記述したjs
hero_asset_abi.js ヒーローアセットのコントラクトアドレスと、abiファイル設定を記述したjs
  land_logic.js マイクリランドの表示用ロジックを記述したjs
land_sector_asset_abi.js 本アプリでは使わず
mch_meta_marking_abi.js 本アプリでは使わず
netowrk.js マイクリapi実行用のメソッドを集めています。jqueryで作成しています
404.html 実行されたURLが存在しないときに開かれるhtml
heroDetail.html ヒーロー情報の詳細html
index.html ランド展示場のトップページのhtml
login.html ランド展示場のログインページのhtml
manifest.json firebaseのトップページ、アイコン情報などを含めたjsonファイル
registPlusMeta.html コメント登録ページ *こちらは、チームメイトのbankerさんに作成していただきました

概要レベルでスマートコントラクトの処理の追加例

1. app/jsフォルダに xxxx_asset_abi.jsというファイル名を用意する
1-1.スマートコントラクトの設定内容を記述します
xxxAssetAbi = ethscan等にあるabiファイルの内容;
xxxAssetAddress = "スマートコントラクトのアドレス";
1-2.htmlかロジック用のjsファイルに内容を記述する

// ヒーローアセットに接続するインスタンスを返す
function getHeroAssetContract() {
// ヒーローアセットの例
const abi = heroAssetAbi;
// スマートコントラクトで通信するアドレスを設定する
const contractAddress = heroAssetAddress;
// 設定しているabiとアドレスで、インスタンスを生成する
const contract = web3js.eth.contract(abi).at(contractAddress);
// ヒーローアセットコントラクトを生成して返す
return contract;
}

// ヒーローアセットのインスタンス生成を呼び出す
heroAsset = getHeroAssetContract();

// ヒーローの一覧総数を取得する場合
// ownerAddressがログインしているアドレス
heroAsset.balanceOf(ownerAddress, (err, result) =>{
// ヒーローの総数に応じて、一覧画面を生成する
});



過去の記事に書いていた通りブロックチェーン×エンタメでの個人的な活動を定期報告をしてまいります。
本ブログ記事が学生さんたちの目に留まってブロックチェーンエンジニア増加に役立ってくれればと思います
jqueryで実装しているので、「フレームワークjsまではまだ...」というかたのお手伝いになれば幸いです
*「もっといい感じに作っているよ」等がある方はどんどん共有してください

web3jsのコード改修もしていきます

2年次の期末試験が全て終わったので、振り返ってみました(後期編)

コロナだよ。
サイバー大学春期の期末試験について、前期分が終わって、つい先日成績発表があったので振り返ります。(・ω・)ノ

受講結果について、スクショでぺたりと貼っていきます

その前に、今回の成績評価科目を一覧で並べてみます

インターネット技術Ⅰ
情報セキュリティ入門
マーケティング入門
eコマース入門
キャリアデザイン
Webデザイン入門
資産運用実践論
脳科学入門
中級英語Ⅱ

今回は、出席日数の科目が少なめですが、9科目あるので前年度と代替同じくらいのペースになります(・ω・)

そして結果は・・・・

f:id:sakuriver:20200303003925p:plain


今回はプライベートもありましたが、8科目は取得できてよかったなと思いました
それでは、以下数が少ないですが振り返りです

KPT

K(Keep)
・期末試験までに授業を全て受講する
・大学2年目で65単位取れたので、3年生で少しペースを上げたら後2~3年で無事卒業なので目指す

P(Problem)
・英語について、時間を取れないで講義を受けきれなかったので受講をしていきたい
・実技の量が物足りないのでもう少し増やしていきたい

T(Try)
・継続科目を取る中に、B評価の科目とかは改善をしていきたい

次回以降は、春からの科目選択考察ブログになりそうです