2024プログラミング:Unityのプログラミング4

スタート画面の作成

スタートシーンを作成します。ProjectウィンドウのScenesフォルダを右クリックしてCreateからSceneを選択します。新規Sceneが作成されるので、StartSceneと名前を付けます。

作成したStartSceneをダブルクリックしてSceneビューの表示を切り替えます。StartSceneに切り替わったら、Hierarchyウィンドウの+ボタンを押してUIからCanvasを選択します。

更にHierarchyウィンドウの+ボタンを押してUIからPanelを選択します。PanelがCanvasの子になったことを確認します。HierarchyウィンドウでPanelを選択し、InspectorウィンドウのRect TransformコンポーネントのScaleでサイズをX、Y、Zそれぞれに2を入力します。PanelのInspectorウィンドウからImageコンポーネントを選択し、Colorの横にある四角のエリアをクリックするとColorウィンドウが開くので任意の色を選択し決定します。

Hierarchyウィンドウの+ボタンを押してUIからText-TextMeshProを選択します。Text(TMP)のInspectorウィンドウで各種設定をします。Rect TransformコンポーネントのPos Xに10、Pos Yに30、Pos Zに0、Widthに200、Heightに40と入力します。TextコンポーネントのTextにゲームタイトルをアルファベットで入力します。Font Sizeに30と入力します。Colorの横にある四角のエリアをクリックするとColorウィンドウが開くので任意の色を選択し決定します。

Hierarchyウィンドウの+ボタンを押してUIからButtonを選択します。ButtonのInspectorウィンドウのRect TransformコンポーネントのPos Xに0、Pos Yに-70、Pos Zに0と入力します。ButtonにUIのTextが子として設定されているので、Buttonの子のTextのInspectorウィンドウのTextコンポーネントにSTARTと入力します。

スクリプトを作成します。Hierarchyウィンドウの+ボタンを押してCreate Emptyを選択して空のゲームオブジェクトを作成し、StartSceneControllerという名称にします。Scriptsフォルダーにスクリプトを作成し、StartSceneControllerという名称にして、StartSceneControllerゲームオブジェクトにアタッチします。
StartSceneControllerゲームオブジェクトのInspectorウィンドウでStartSceneControllerスクリプトがアタッチできているか確認しておきます。
StartSceneControllerスクリプトに以下のコードを記述します。

StartSceneController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;//追加

public class StartSceneController : MonoBehaviour
{
     public void OnGameStart()
     {
           SceneManager.LoadScene("GameScene");
     }
}

STARTボタンの設定をします。ButtonのInspectorウィンドウを表示し、Buttonコンポーネントを表示します。
On Click()の+ボタンをクリックしてから◉をクリックし、Select Objectウィンドウを表示し検索欄に、StartSceneControllerと入力しSceneタブを選び、StartSceneControllerをダブルクリックします。次にメソッドの呼び出しを設定します。No Functionから、StartSceneControllerを選択し、OnGameStart( )をクリックします。これで、STARTボタンを押すと、StartSceneController.csスクリプト内のOnGameStartメソッドが呼び出されるように設定できました。

OnGameStartメソッドの処理でシーン StartSceneから、シーン GameSceneをロードして実行できるように設定します。Unityの画面上部のメニューからFileを選択し、Build Settingsをクリックします。
Build Settingsウィンドウが表示されるので、ProjectウィンドウのStartSceneをScenes in Buildにドラッグアンドドロップします。
Scenes in BuildにScenes/StartSceneが追加されたのを確認したら、次にGameSceneをドラッグアンドドロップします。
Scenes/StartSceneの下にScenes/GameSceneが追加できているのを確認したら、Build Settingsウィンドウを閉じます。

HierarchyウィンドウにStartSceneが表示されている状態でゲームを実行するとスタート画面からゲームが開始されます。STARTボタンを押してゲームが開始されるか確認してみましょう。

リザルト画面の作成

ProjectウィンドウのScenesフォルダを右クリックしてCreateからSceneを選択します。新規Sceneが作成されるので、ResultSceneと名前を付けます。
作成したResultSceneをダブルクリックしてSceneビューの表示を切り替えます。

ResultSceneに切り替わったら、Hierarchyウィンドウの+ボタンを押してUIからCanvasを選択します。更にHierarchyウィンドウの+ボタンを押してUIからPanelを選択します。PanelがCanvasの子になったことを確認します。HierarchyウィンドウでPanelを選択し、InspectorウィンドウのRect TransformコンポーネントのScaleでサイズをX、Y、Zそれぞれに2を入力します。PanelのInspectorウィンドウからImageコンポーネントを選択し、Colorの横にある四角のエリアをクリックするとColorウィンドウが開くので任意の色を選択し決定します。Hierarchyウィンドウの+ボタンを押してUIからText-TextMeshProを選択します。Hierarchyウィンドウに追加されたText(TMP)を複製しText(TMP)を3つ作成します。Text(TMP)の名称は、Text、Text(TMP)(1)、Text(TMP)(2)になります。

Text(TMP)のInspectorウィンドウで設定します。PosX 0、PosY 80、PosZ 0、Width 230、Height 40と入力します。TextコンポーネントのTextエリアに適切な文書を記載します。(GOAL!!など)ColorエリアでTextに任意の色を設定しておきます。

Text(TMP)(1)のInspectorウィンドウで設定します。PosX -5、PosY 20、PosZ 0、Width 160、Height 30と入力します。TextコンポーネントのTextエリアにTOTAL SCOREと記載します。ColorエリアでTextに任意の色を設定しておきます。

Text(TMP)(2)をScoreTextと名称変更します。ScoreTextのInspectorウィンドウで設定します。PosX 130、PosY 20、PosZ 0、Width 160、Height 30と入力します。TextコンポーネントのTextエリアに0と記載します。ColorエリアでTextに任意の色を設定しておきます。

Hierarchyウィンドウの+ボタンを押してUIからButtonを選択します。ButtonのInspectorウィンドウのRect TransformコンポーネントのPos Xに0、Pos Yに-70、Pos Zに0と入力します。ButtonにUIのTextが子として設定されているので、Buttonの子のTextのInspectorウィンドウのTextコンポーネントにRESTARTと入力します。

スクリプトを作成します。Hierarchyウィンドウの+ボタンを押してCreate Emptyを選択して空のゲームオブジェクトを作成し、ResultSceneControllerという名称にします。Scriptsフォルダーにスクリプトを作成し、ResultSceneControllerという名称にして、ResultSceneControllerゲームオブジェクトにアタッチします。
ResultSceneControllerゲームオブジェクトのInspectorウィンドウでResultSceneControllerスクリプトがアタッチできているか確認しておきます。

GameSceneControllerスクリプトの変数宣言部分を確認します。

GameSceneController.cs (一部抜粋)

public class GameSceneController : MonoBehaviour 
{ 
     public static int score = 0; //変数のグローバル化
     private Text scoreText;

ResultSceneControllerスクリプトに以下のコードを記述します。

ResultSceneController.cs

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

public class ResultSceneController : MonoBehaviour
{
    private TextMeshProUGUI scoreText;

    void Start()
    {
        GameObject textObject = GameObject.Find("ScoreText");
        scoreText = textObject.GetComponent<TextMeshProUGUI>();
        scoreText.text = GameSceneController.score.ToString(); ;
    }

    public void OnGameStart()
    {
        SceneManager.LoadScene("GameScene");
    }
}

RESTARTボタンの設定をします。ButtonのInspectorウィンドウを表示し、Buttonコンポーネントを表示します。
On Click()の+ボタンをクリックしてから◉をクリックし、Select Objectウィンドウを表示し検索欄に、ResultSceneControllerと入力しSceneタブを選び、ResultSceneControllerをダブルクリックします。次にメソッドの呼び出しを設定します。No Functionから、ResultSceneControllerを選択し、OnGameStart( )をクリックします。これで、RESTARTボタンを押すと、ResultSceneController.csスクリプト内のOnGameStartメソッドが呼び出されるように設定できました。

OnGameStartメソッドの処理でシーン ResultSceneから、シーン GameSceneをロードして実行できるように設定します。Unityの画面上部のメニューからFileを選択し、Build Settingsをクリックします。
Build Settingsウィンドウが表示されるので、ProjectウィンドウのResultSceneをScenes in Buildにドラッグアンドドロップします。
Scenes/StartSceneの下にScenes/GameScene、その下にScenes/ResultSceneが追加できているのを確認したら、Build Settingsウィンドウを閉じます。

HierarchyウィンドウにResultSceneが表示されている状態でゲームを実行するとリザルト画面からゲームが開始されます。RESTARTボタンを押してゲームが再開されるか確認してみましょう。

ゴール処理の作成

ProjectウィンドウのScenesフォルダにあるGameSceneをダブルクリックしてHierarchyウィンドウの表示をGameSceneに切り替えます。Roadゲームオブジェクトの1つを複製(Duplicate)し適切な場所に配置し、Goalという名称に変更します。
ProjectウィンドウのScriptsフォルダにGoalスクリプトを作成し、Goalゲームオブジェクトにアタッチしておきます。

各スクリプトに必要なコードを追記し変更します。

Controller.cs

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

public class Controller : MonoBehaviour
{
   private float speed = 3f;
   private float verticalInput = 0f;
   private Rigidbody CharacterRigidbody;
   private bool isStop = false;

   void Start()
   {
      CharacterRigidbody = GetComponent<Rigidbody>();
      isStop = false;
   }

   void Update()
   {
      if (isStop)
      {
         return;
      }

      if (transform.position.y < 0.5)
      {
         isStop = true;
         GameObject gameSceneController = GameObject.Find("GameSceneController");
         GameSceneController script = gameSceneController.GetComponent<GameSceneController>();
         script.OnFailed();
      }
      else
      {
         verticalInput = Input.GetAxis("Vertical");

         if (Input.GetKey("right"))
         {
            transform.Rotate(0, 10, 0);
         }
         else if (Input.GetKey("left"))
        {
            transform.Rotate(0, -10, 0);
        }
      }
   }

   void FixedUpdate()
   {
      if (isStop)
      {
         return;
      }

      if (CharacterRigidbody.velocity.sqrMagnitude < 5f)
      {
          Vector3 CharacterForward = CharacterRigidbody.transform.forward;
          Vector3 moveVector = speed * (CharacterForward * verticalInput);
          moveVector.y = 0;
          CharacterRigidbody.AddForce(moveVector, ForceMode.Impulse);
      }
   }
  //追記ここから=====>
   public void OnStop()
   {
      isStop = true;
   }
  //======>ここまで
}

GameSceneController.cs

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

public class GameSceneController : MonoBehaviour
{
   public static int score = 0;
   private Text scoreText;

   void Start()
   {
      GameObject gameScoreText = GameObject.Find("ScoreText");
      scoreText = gameScoreText.GetComponent<Text>();
      score = 0;
   }

   public void OnFailed()
   {
      Invoke("OnLoadGameScene", 1.5f);
   }

   private void OnLoadGameScene()
   {
      SceneManager.LoadScene("GameScene");
   }

   public void AddScore(int num)
   {
      score += num;
      scoreText.text = score.ToString();
   }
   //追記=====>
   public void OnGoal()
   {
      GameObject Character = GameObject.Find("Character");
      Controller script = Character.GetComponent<Controller>();
      script.OnStop();
      Invoke("OnLoadResultScene", 1.5f);
   }

   private void OnLoadResultScene()
   {
      SceneManager.LoadScene("ResultScene");
   }
   //=====>ここまで
}

Goal.cs

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

public class Goal : MonoBehaviour
{
   void OnCollisionEnter(Collision collision)
   {
      GameObject controller = GameObject.Find("GameSceneController");
      GameSceneController script = controller.GetComponent<GameSceneController>();
      script.OnGoal();
   }
}

ゲームキャラクターを動かしてゴールさせてみましょう。リザルト画面にシーンが遷移したら成功です。

RESTARTボタンを押すとGameSceneに戻ることができます。

2024プログラミング:Unityのプログラミング3

再スタート機能の追加

再スタート機能を追加します。ProjectウィンドウのAssetsフォルダの中、Scenesフォルダ内のSampleSceneをリネームし、GameSceneと名称変更します。(右クリックしてRenameを選択し、名称変更)

Scene全体を管理するスクリプトを作成するため、空のゲームオブジェクトを作成します。
Heerarchyウィンドウの+ボタンを押し、Create Emptyを選択します。GameObjectという名称のゲームオブジェクトが作成されるので、GameSceneControllerという名称に変更します。

ProjectウィンドウのAssetsフォルダの中、ScriptsフォルダにGameSceneControllerという名称のスクリプトを作成し、ゲームオブジェクトGameSceneControllerにアタッチします。

スクリプトGameSceneControllerを開き、スクリプトを記述します。

GameSceneController.cs

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

public class GameSceneController : MonoBehaviour
{
  public static int score = 0;
  private Text scoreText;

  void Start()
  {
     GameObject gameScoreText = GameObject.Find("ScoreText");
     scoreText = gameScoreText.GetComponent<Text>();
     score = 0;
  }

  public void OnFailed()
  {
     Invoke("OnLoadGameScene", 1.5f);
  }

  private void OnLoadGameScene()
  {
     SceneManager.LoadScene("GameScene");
  }

  public void AddScore(int num)
  {
     score += num;
     scoreText.text = score.ToString();
  }

}

落下判定の処理をControllerスクリプトに追記します。

Controller.cs

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

public class Controller : MonoBehaviour
{
     private float speed = 3f;
     private float verticalInput = 0f;
     private Rigidbody CharacterRigidbody;
     private bool isStop;//追記

     void Start()
     {
          CharacterRigidbody = GetComponent<Rigidbody>();
          isStop = false;//追記
     }

     void Update()
     {
     //追記=====>
          if (isStop)
          {
              return;
          }

          if (transform.position.y < 0.5)
          {
               isStop = true;
               GameObject gameSceneController = GameObject.Find("GameSceneController");
               GameSceneController script = gameSceneController.GetComponent<GameSceneController>();
               script.OnFailed();
           }//=====>追記
           else//追記
          {//追記
               verticalInput = Input.GetAxis("Vertical");
               if (Input.GetKey("right"))
               {
                    CharacterRigidbody.transform.Rotate(0, 10, 0);
               }
               else if (Input.GetKey("left"))
              {
                   CharacterRigidbody.transform.Rotate(0, -10, 0);
              }
          }//追記
       }

       void FixedUpdate()
       {
            //追記=====>
            if (isStop)
            {
                 return;
            }
            //=====>追記
            if (CharacterRigidbody.velocity.sqrMagnitude < 5f)
            {
                 Vector3 CharacterForward = CharacterRigidbody.transform.forward;
                 Vector3 moveVector = speed * (CharacterForward * verticalInput);
                 moveVector.y = 0;
                 CharacterRigidbody.AddForce(moveVector, ForceMode.Impulse);
            }
       }

}

キャラクターがステージから落下するとリスタートする機能が追加されました。動作確認してみましょう。

アイテムの作成
報酬となるアイテムを作成します。ゲームオブジェクトを作成し、名称をItemとし、マテリアルを設定しておきます。

Itemゲームオブジェクトをステージに配置し、inspectorウィンドウのColliderコンポーネントのIs Triggerにチェックマークを付けます。
Colliderコンポーネントはアタッチするゲームオブジェクトの形状によって特性が変化します(BoxColliderやSphere Colliderなど)。

ColliderコンポーネントのIs Triggerにチェックを入れることで、オブジェクトは重力などの物理法則を影響を受けなくなり、衝突判定が取得できるようになります。
Is Triggerが有効なゲームオブジェクトが他のゲームオブジェクトに接触すると、OnTriggerEnterメソッドが呼び出されます。

Itemスクリプトの作成
ScriptsフォルダにItemという名称のスクリプトを作成し、ゲームオブジェクトItemにアタッチします。Itemスクリプトに以下の内容を記述します。

Item.cs

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

public class Item : MonoBehaviour
{
     void OnTriggerEnter(Collider other)
     {
          Destroy(gameObject);//画面からゲームオブジェクトを消す
     }
}

スクリプトがアタッチされたゲームオブジェクトItemをプレファブ機能を使ってインスタンスとして複製します。
ProjectウィンドウのAssetsフォルダに新しくPrefabsという名称のフォルダを作成します。
PrefabsフォルダにHierarchyウィンドウのItemゲームオブジェクトをドラッグアンドドロップで移動します。
Prefabsフォルダに移動したItemゲームオブジェクトをHierarchyウィンドウにドラッグアンドドロップして戻すと、Itemインスタンスが作成されます。同じようにして、Prefabsフォルダ内のItemゲームオブジェクトをHierarchyウィンドウに必要な数だけドラッグアンドドロップしてItemインスタンスを複数生成します。

アイテムにキャラクターが衝突すると画面から消えるようになりました。動作確認してみましょう。

画面にスコアを表示する
UI(ユーザーインターフェース)のCanvasを作成します。Hierarchyウィンドウの+ボタンを押して、UIからCanvasを選択します。
CanvasのInspectorウィンドウのScale FactorからScale With Screen Sizeを選択し、X 500,Y400に設定します。
次に文字を表示するために、Hierarchyウィンドウの+ボタンを押して、UIからTextを選択します。TextのInspectorウィンドウのRect TransformコンポーネントにPos X 180, Pos Y 110, PosZ 0と入力します。TextエリアにSCOREと入力するとゲーム画面に表示されます。Font Sizeは20〜24ポイントにしておきます。

続けて、HierarchyウィンドウのCanvasの子であるUI要素のTextを複製(Duplicate)します。複製されたText(1)の名称をScoreTextに変更します。
ScoreTextのInspectorウィンドウのRect TransformコンポーネントにPos X 280, Pos Y 110, PosZ 0と入力し、Textエリアに数字の0を入力しておきます。

アイテムを取得するとスコアが加算される処理をItemスクリプトに追記します。

Item.cs

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

public class Item : MonoBehaviour
{
     void OnTriggerEnter(Collider other)
     {
          GameObject gameSceneController = GameObject.Find("GameSceneController"); //追記
          GameSceneController script = gameSceneController.GetComponent<GameSceneController>();//追記 
          script.AddScore(100); //追記

          Destroy(gameObject);
     }
}

アイテムを取得するとスコアが100点加算されるようになりました。動作確認してみましょう。

参考資料
Unityの2021年以降のバージョンでは、テキストを扱うにはTextMeshProが使用されます。TextMeshProを使ったコードを以下に記載します。

GameSceneController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using TMPro;//追記

public class GameSceneController : MonoBehaviour
{
  public static int score = 0;
  private TextMeshProUGUI scoreText;//変更

  void Start()
  {
     GameObject gameScoreText = GameObject.Find("ScoreText");
     scoreText = gameScoreText.GetComponent<TextMeshProUGUI>();//変更
     score = 0;
  }

  public void OnFailed()
  {
     Invoke("OnLoadGameScene", 1.5f);
  }

  private void OnLoadGameScene()
  {
     SceneManager.LoadScene("GameScene");
  }

  public void AddScore(int num)
  {
     score += num;
     scoreText.text = score.ToString();
  }

}

Item.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;//追記

public class Item : MonoBehaviour
{
     void OnTriggerEnter(Collider other)
     {
          GameObject gameSceneController = GameObject.Find("GameSceneController"); 
          GameSceneController script = gameSceneController.GetComponent<GameSceneController>(); 
          script.AddScore(100); 

          Destroy(gameObject);
     }
}

2024プログラミング:Unityのプログラミング2

ゲームステージの拡張
ゲームのステージに動くオブジェクトを追加して拡張していきます。

動く道
動く道を追加します。ヒエラルキーウィンドウのRoadを複製し、インスペクターウィンドウでMoveRoadという名称に変更します。
MoveRoadにRigidbodyコンポーネントをアタッチします。RigidbodyのUse Gravityのチェックマークを外し、Is Kinematicにチェックマークをつけます。
ConstraintsのFreeze RotationのX,Y,Zにチェックマークを付けます。

ScriptsフォルダにMoveRoadという名前のスクリプトを作成します。MoveRoadスクリプトをゲームオブジェクトMoveRoadにドラッグ・アンド・ドロップします。MoveRoadスクリプトがゲームオブジェクトMoveRoadにアタッチできているかをインスペクターウィンドウで確認します。

MoveRoadスクリプトに以下のコードを記述します。

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

public class MoveRoad : MonoBehaviour
{
    private Rigidbody roadRigid;
    private Vector3 defPos;

    void Start()
    {
        roadRigid = GetComponent<Rigidbody>();
        defPos = transform.position;
    }

    void Update()
    {
        
    }

    void FixedUpdate()
    {
        roadRigid.MovePosition(
            new Vector3(defPos.x , defPos.y, defPos.z + Mathf.PingPong(Time.time, 4)));
    }
}

スクリプトの動作確認をします。

MovePositionメソッドを使用して、ゲームオブジェクトの座標を移動させています。

Rigidbody型の変数.MovePosition(移動先の座標)

Mathf.PingPomgメソッドを使用して往復する動きを作ります。

Mathf.PingPong(時間,数値)

Mathf.PingPong(Time.time, 4)の記述を、それぞれ別の座標に足すことで、移動方向を変更できます。
また数値を変更することで、移動距離を変更できます。

例)上下方向に倍の距離を移動する道を作成する。

roadRigid.MovePosition(
            new Vector3(defPos.x , defPos.y + Mathf.PingPong(Time.time, 8), defPos.z ));

 

障害物の作成
キャラクターを突き落とす障害物を作成します。ヒエラルキーウィンドウに新たなキューブ(立方体)を追加し、インスペクターウィンドウで、Obstacleと名前を付けます。マテリアルを作成してアタッチしておきます。Rigidbodyコンポーネントをアタッチし、Massの値を10000にし、Use Gravityのチェックマークを外し、ConstraintsのFreeze RotationのX,Y,Zにチェックマークを付けます。

ScriptsフォルダにObstacleという名前のスクリプトを作成します。ObstacleスクリプトをゲームオブジェクトObstacleにドラッグ・アンド・ドロップします。ObstacleスクリプトがゲームオブジェクトObstacleにアタッチできているかをインスペクターウィンドウで確認します。
Obstacleスクリプトに以下のコードを記述します。

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

public class Obstacle : MonoBehaviour
{
    private Rigidbody obstacleRigid;
    private float lastTime = 0.0f;
    private float timeInterval = 4.0f;
    private float speed = 100f;
    private float direction = -1.0f;

    void Start()
    {
        obstacleRigid = GetComponent<Rigidbody>();
        lastTime = Time.time;
        direction = -1.0f;
    }

    void FixedUpdate()
    {
        if (Time.time >= lastTime + timeInterval)
        {
            lastTime = Time.time;
            direction *= -1.0f;
            obstacleRigid.velocity = Vector3.zero;
        }

        Vector3 bearForward = obstacleRigid.transform.right;
        Vector3 moveVector = speed * (bearForward * direction);
        moveVector.y = 0;
        moveVector.z = 0;
        obstacleRigid.AddForce(moveVector, ForceMode.Impulse);
    }
}

スクリプトの動作確認をします。

transformメソッドは、transform.rightで水平方向、transform.forwardで奥行き方向、transform.upで上下方向に座標を移動させることができます。

例)奥行き方向の移動

Vector3 bearForward = obstacleRigid.transform.forward;
        Vector3 moveVector = speed * (bearForward * direction);
        moveVector.x = 0;
        moveVector.y = 0;
        obstacleRigid.AddForce(moveVector, ForceMode.Impulse);

例)上下方向の移動

Vector3 bearForward = obstacleRigid.transform.up;
        Vector3 moveVector = speed * (bearForward * direction);
        moveVector.x = 0;
        moveVector.z = 0;
        obstacleRigid.AddForce(moveVector, ForceMode.Impulse);

2024プログラミング:Unityのプログラミング1

いよいよ、Unityのプログラミングを進めていきます。

ゲームオブジェクトの配置
Sceneビューに以下のゲームオブジェクトを配置してください。

Plane: ゲームのベースになります。Inspectorで名称をFloorとし、PositionをX:0,Y:0,Z:0、RotationをX:0,Y:0,Z:0、ScaleをX:10,Y:10,Z:10とします。

Cube: ゲームのステージになります。Inspectorで名称をRoadとし、PositionをX:2,Y:0,Z:6、RotationをX:0,Y:90,Z:0、ScaleをX:15,Y:1,Z:3とします。

Cube: ゲームのキャラクターになります。Inspectorで名称をCharacterとし、PositionをX:2,Y:1,Z:0、RotationをX:0,Y:0,Z:0、ScaleをX:1,Y:1,Z:1とします。

それぞれに、異なるMaterial(色)を設定しておきます。

ゲームオブジェクトにコンポーネントをアタッチする
UnityのSceneビューに配置されているものは、全てゲームオブジェクトと呼びます。
ゲームオブジェクトにコンポーネントと呼ぶ部品を接続することで色々な機能が使えるようになります。
ゲームオブジェクトにコンポーネントやマテリアル、スクリプトを接続することをアタッチと呼びます。

Character ゲームオブジェクトにコンポーネントを追加します。
Character のInspectorで Add Componentを選択し、Rigidbodyを選択します。RigidbodyのウィンドウのConstraintsを開き、Freeze RotationのX,Y,Z全ての項目にチェックマークを記載します。この設定によりゲームオブジェクトは重力などの物理法則に従った動きをするようになります。Freeze Rotationの設定は、物理法則に従う際に回転方向への影響を防ぎ、不自然な動きを抑えるものです。

ゲームオブジェクトとカメラを親子関係にする
HierarchyウィンドウでMain Cameraを選択し、InspectorでPositionをX:0,Y:0,Z:-3、RotationをX:0,Y:0,Z:0、ScaleをX:1,Y:1,Z:1とします。

Main CameraをCharacter ゲームオブジェクトにドラッグアンドドロップします。この操作によって、Main CameraがCharacter ゲームオブジェクトに追随するようになります。

スクリプトの記述
スクリプト(Script)とは、簡易的なプログラミング言語のことです。Unityではゲームオブジェクトにスクリプトをアタッチすることで、様々なふるまいをゲームオブジェクトに実行させることができます。

ProjectウィンドウでFolderを追加し、名称をScriptsとします。Scriptsフォルダーを右クリックし、CreateメニューからC#Scriptを選択します。作成されたC#ScriptファイルにControllerという名称をつけます。

Controllerファイルをダブルクリックして展開すると、Visual Studioというプログラミング用のエディターというソフトウェアが開きます。

最初に開いた状態で、Visual Studioには以下のようにスクリプトが記述されています。

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

public class Controller : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

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

    }
}

以下の部分は各種機能の使用を宣言する部分です。

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

以下の部分はクラスと呼ばれる処理の括りとなります。

public class Controller : MonoBehaviour
{

}

以下の部分は、それぞれメソッドと呼ばれ、スクリプト内での実行のタイミングが決まっています。

Startメソッド:ゲームオブジェクトが出現した時に実行する

void Start()
    {

    }

Updateメソッド:フレームごとに実行する

void Update()
    {

    }

ゲームのプログラミング
エディターに以下の記述を加えていきます。

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

public class Controller : MonoBehaviour
{
    private float speed = 3f;
    private float verticalInput = 0f;
    private Rigidbody CharacterRigidbody;

    void Start()
    {
        CharacterRigidbody = GetComponent<Rigidbody>();
    }

    void Update()
    {
        verticalInput = Input.GetAxis("Vertical");

        if (Input.GetKey("right"))
        {
            CharacterRigidbody.transform.Rotate(0, 10, 0);
        }
        else if (Input.GetKey("left"))
        {
            CharacterRigidbody.transform.Rotate(0, -10, 0);
        }
    }

    void FixedUpdate()
    {
         if (CharacterRigidbody.velocity.sqrMagnitude < 5f)
         {
            Vector3 CharacterForward = CharacterRigidbody.transform.forward;
            Vector3 moveVector = speed * (CharacterForward * verticalInput);
            moveVector.y = 0;
            CharacterRigidbody.AddForce(moveVector, ForceMode.Impulse);
         }
    }

}

 

プログラムの解説

変数の宣言
変数の型を決めて、変数名を宣言します。
変数の「値」を設定しておくこともできます。

private float speed = 3f;
private float verticalInput = 0f;
private Rigidbody CharacterRigidbody;

GetComponentメソッド
<>内に取得したいコンポーネント名を記載して使用します。

CharacterRigidbody = GetComponent<取得したいコンポーネント名>();

Updateメソッド
Vertical(上下)、right(右)、left(左)それぞれのキーに対応する動きを設定しています。

Verticalキーの上向きボタンを押下すると0.01fの小さい値が入力され、押下し続けると最大で1.0fの値が入力されます。下向きボタンを押下すると、-0.01fの小さい値が入力され、押下し続けると最小で-1.0fの値が入力されます。押されていない時は0.0fの値が入力されます。

verticalInput = Input.GetAxis("Vertical");

if文(条件分岐)
プログラミングは通常は最初の1行から終わりの1行まで順番に処理されます。しかし、それでは複雑な処理をコンピューターに実行させることができません。複雑な処理をさせるための方法のひとつとして条件分岐という考え方が存在します。

単純な処理:
公園に行く。

複雑な処理:
晴れたら公園に行く。
そうでなければ、家で読書する。

上の例のように、条件によって行動(=処理)が変化する(=分岐する)ことを条件分岐といいます。上の複雑な処理の例は以下のように条件分岐命令の書式で記述することができます。

if( 晴れ ){ 公園に行く; }else{ 家で読書する; }

コードであらわす場合は以下のように記述します。

if( 条件式 ){ 条件式が真の場合に実行する内容; }else{ 条件式が真ではない場合に実行する内容; }

プログラミングでは、真であることをtrueと記述します。また真ではない場合は偽とあらわしfalseと記述します。

if〜else文

if文の条件を満たさなかった場合の処理を明示する場合は、以下のように記述することができます。

if( 条件式 ){ 条件がtrueの場合に実行される内容; }else{ 条件がfalseの場合に実行される内容; }

条件式
真偽を判定するために使用できる条件式を以下に記載します。

< 左辺が右辺より小さい

i<100

> 左辺が右辺より大きい

i>100

<= 左辺が右辺以下

i<=100

>= 左辺が右辺以上

i>=100

>== 左辺が右辺に等しい

i>==100

!= 左辺が右辺に等しくない

i>!=100

&& かつ (「かつ」はandとも表現する)

i>0 && i<100

|| または (「または」はorとも表現する)

i<0 || i>100

Unityのスクリプトでは、右ボタンが押下されたら10度回転、そうでない場合は、-10度回転するようにif文で記述しています。

if (Input.GetKey("right"))
{
    CharacterRigidbody.transform.Rotate(0, 10, 0);
}
else if (Input.GetKey("left"))
{
    CharacterRigidbody.transform.Rotate(0, -10, 0);
}

追加の処理(ジャンプボタンの追加)
以下のif文を追加してみましょう。

if (Input.GetKey("a"))
{
    CharacterRigidbody.position += Vector3.up * 0.05f;
}

FixedUpdateメソッド
キャラクターの移動処理を記述します。

if (CharacterRigidbody.velocity.sqrMagnitude < 5f)
{
   Vector3 CharacterForward = CharacterRigidbody.transform.forward;
   Vector3 moveVector = speed * (CharacterForward * verticalInput);
   moveVector.y = 0;
   CharacterRigidbody.AddForce(moveVector, ForceMode.Impulse);
}

AddForceメソッド
Rigidbodyコンポーネントがアタッチされているゲームオブジェクトに力を加えて動かす。

Rigidbody型の変数.AddForce(力の強さ,力のかけ方)

力のかけ方:
ForceMode.Force 重さあり、継続的に力を加える
ForceMode.Impulse 重さあり、瞬間的に力を加える
ForceMode.Acceleration 重さ無視、加速度を設定
ForceMode.VerocityChange 重さ無視、速度を設定

Visual Studioでスクリプトが記述できたら保存して、Unityの操作画面に戻り、トップバーのゲーム実行ボタンを押して動作確認してみる。

2024プログラミング:Unityの基礎

Unityの画面レイアウト変更
3DCGゲームが作りやすいようにUnityの画面レイアウトを変更する。Unityの画面右上のLayout(レイアウト)ボタンから、2by3を選択する。

Unityの画面レイアウトについて
Sceneビュー
ゲーム作成用のステージ画面。ゲームオブジェクトを配置してゲームを作成する。
Gameビュー
ゲームを作成し、実行した時の見え方を確認できる。Sceneビューに配置したカメラを調整することで見え方を変更できる。
Hierarchyウィンドウ
Sceneビューに配置したゲームオブジェクトのリストが表示される。
Projectウィンドウ
プロジェクトで使用するアセットを管理する。
Inspectorウィンドウ
選択しているゲームオブジェクトやアセットの詳細情報が表示される。
ツールバー
Sceneビューでの各種操作や、ゲームの実行・停止などのボタンが配置されている。

3Dゲームオブジェクトの作成
Hierarchyウィンドウ上部の+ボタンをクリックし、3D Objectに続けて、配置したい基本形状を選択するとSceneビューに配置される。Sceneビューには座標があり、X軸=横幅、Y軸=奥行き、Z軸=高さの3つの要素で空間が構成されている。
ゲームオブジェクトの移動・回転・拡大縮小
ツールバーのMove、Rotate、Scaleいずれかのボタンを選択すると、ゲームオブジェクトの周囲に操作用の表示があるので、それぞれ操作する。
Transformコンポーネントの操作
InspectorウィンドウのTransformコンポーネントのMove、Rotate、Scaleにそれぞれ数値を入力して操作する。

Sceneビューの視点変更
ズームイン・ズームアウト
マウスの中ボタンをホイールとして使い回転させる。
視点の範囲変更
ツールバーのHand Toolボタンを押してSceneビュー内をドラッグする。
視点の角度変更
Sceneビュー上でAltキーを押し、Hand Toolの表示が目の形のアイコンになったらSceneビュー内をドラッグする。
シーンギズモの操作
Sceneビュー右上に表示されるシーンギズモのXYZの円錐形ボタンをクリックすると、クリックした軸が正面を向く。
Shiftキーを押しながらシーンギズモの中心の立方体をクリックすると2軸と3軸の見え方を交互に切り替えられる。

マテリアルの設定
マテリアル(Material)は色や材質をゲームオブジェクトに設定するためのものである。
ゲームオブジェクトにマテリアルを設定する。
・Projectウィンドウ内にあるAssetsフォルダを右クリックし、Create > Folderをクリックする。
・Projectウィンドウ内のAssetsフォルダ内に新しいフォルダが作成されるので、フォルダ名をMaterialsにする。
・Materialsフォルダを右クリックしCreate > Materialをクリックする。
・新規のマテリアルが作成されるので、任意の名前を付けておく。
・マテリアルを選択した状態で、マテリアルのInspectorウィンドウのAlbedoの横の四角い枠をクリックするとColorウィンドウが開くので色の設定をする。
・その他の項目についても設定しマテリアルを完成させる。
・完成したマテリアルをHierarchyウィンドウのゲームオブジェクトにドラッグ・アンド・ドロップするとゲームオブジェクトにマテリアルを関連づけることができる。

2024プログラミング準備編2:マテリアル設定とレンダリング

マテリアルの設定

3CCGで制作するオブジェクトの表面にマテリアルの設定を施すことで、よりリアルな表現が可能になります。

Blenderでは、以下の手順でオブジェクトにマテリアルを設定します。

1. シェーディングのモードを切り替える

3Dビューの右上にある、シェーディングモードの選択ボタンから、マテリアルプレビューモードのボタンをクリックしてシェーディングモードを切り替えます。

2. マテリアルプロパティの設定

プロパティからマテリアルプロパティを選択し、「新規」ボタンをクリックする。新しく作成された「マテリアル」の名称を変更する。(マテリアルと表記された箇所をダブルクリックする。)
マテリアルに各種設定を施す。

・ベースカラー
基本となる色のこと。
・メタリック
金属の性質を示す数値です。値が1に近いほど金属質になり、0に近いほど非金属質となります。
・スペキュラー
鏡面反射の度合いを示す数値です。値が1に近いほどハイライトが強くなり、0に近いほどハイライトが弱くなります。
・粗さ
表面の荒さを示す数値です。1に近いほど粗く、0だと平滑となり周囲の景色が強く映り込みます。
・伝播
透明度を示す数値です。1に近いほど透明度が高く、0は不透明となります。
・IOR
光の屈折率を示す数値です。伝播が0で不透明なオブジェクトには効果が表れません。

反射などのマテリアル属性を確認するには、レンダープロパティを選択し、スクリーンスペース反射にチェックを入れます。

作成したマテリアルを複数のオブジェクトに適用するには、アウトライナーから、対象とするマテリアルを選んで、3Dビューワー上のオブジェクトにドラッグ・アンド・ドロップして設定する。

カメラとライトの設定

1. 3Dビューの右上にある、シェーディングモードの選択ボタンから、レンダープレビューモードのボタンをクリックしてシェーディングモードを切り替えます。

2. アウトライナーからライト(Light)を選択し、適切な位置に配置します。

3. ナビゲートメニューからカメラをクリックしてカメラビューに切り替えます。

3Dビューの右端の三角ボタンをクリックするか、キーボードのNボタンを押してサイドメニューを呼び出し、ビューを選択しビューのロックから、カメラをビューにロックする項目にチェックを入れておきます。

カメラに対象のオブジェクトが適切に配置されるようにレイアウトします。

レンダリング

レンダリング(Rendering)とは、各種設定によって完成した3次元立体空間の画像または映像を出力することです。コンピューターの処理能力にもよりますが、レンダリングには非常に多くの処理時間が必要です。

トップバーのレンダーメニューから画像をレンダリングを選択すると、レンダリングが開始されます。

レンダリングが完成するとビューワーが開き、レンダリング後の画像が表示されます。ビューワーの画像メニューから、名前を付けて保存を選択すると、保存の設定画面が開きます。ファイルフォーマットから適切な画像データ形式を選択し、ファイル名を付けて保存先を指定して保存します。

データのエクスポート

Blenderで作成した3DCGモデルをUnityで使用できるように書き出してみましょう。トップバーのメニューから、ファイル>エクスポート>FBXを選択します。(FBXはファイル形式の名称です。)
ファイルビューウィンドウが開くので、保存場所を指定し、ファイル名を指定し(XXX.fbxのXXXの部分を任意の名称にする)、オブジェクトタイプから、カメラとランプ以外の項目を選択して、エクスポートボタンをクリックします。

保存されたFBX形式のデータ(XXX.fbx)を、UnityのProjectウィンドウにドラッグ&ドロップすることで、Unityで使用できるようになります。

練習課題

Blenderで作成した動物モデルをレンダリングし、画像として出力して
提出してください。
・PNGデータで提出すること。
・提出場所:CS レポート掲示板 
  プログラミングI「動物モデルのレンダリング画像提出」スレッド
・締切:2024年10月14日(月)まで

2024プログラミング準備編1:3DCG制作環境に慣れる

3DCG制作について

3DCGは、3 Dimensional Computer Graphics の略語で、3次元コンピューターグラフィックスなどと翻訳されています。

コンピューターの情報処理能力の向上によって、写真やイラストなどのグラフィックス表現が、コンピューター上で可能となりました。
3DCGもコンピューターグラフィックスの一種ですが、仮想的な3次元空間(立体空間)をコンピューター上で構築し、奥行き感のある表現として実現することができます。

3DCGを制作する環境

3DCGを制作するためには、専用のソフトウェアが必要になります。利用者が多い3DCG制作ソフトとして、映画などの現場で利用が多い、Autodesk MAYA、ゲームなどの現場で利用が多い、Autodesk 3ds Max、CADと呼ばれる製品設計の現場で利用が多い、Autodesk Fusion 360、建築などの現場で利用が多い、Autodesk AutoCAD などがあります。その他にも、粘土を捏ねて造形するかのように3DCGを制作できるPixologic ZBrushや、Sculptris などがあります。

ここで紹介した以外にも様々な3DCG制作用のソフトウェアが用途に合わせて利用されています。

この講座では、3DCG制作用ソフトとしてBlenderを使用します。

Blenderについて

Blenderは、統合型の3DCG制作環境で、モデリングから、アニメーション制作、動画編集まで、対応しています。高機能でありながら、フリーウェアであり、ライセンス料が無料であるのが特長です。
他の有料ソフトウェアに比べると、規模の大きいプロジェクトなどで使用されることが少ないのですが、いくつかの映像プロダクションやゲーム制作会社などでの採用実績があります。

Blender公式WEBサイト

Blenderの日本語環境設定

Blenderはデフォルトでは英語環境になっているので、日本語環境に変更する。
・左上のメニューから「Edit」を選択。
・「Edit」メニューの中から「Preferences」を選択。
・「Interface」項目から「Translation」にチェックを入れる。
・「Translation」の「Language」プルダウンメニューから「Japanese(日本語)」を選択し、「Tooltips」と「Interface」にチェックを入れる。
以上でBlenderを日本語環境に設定できます。

Blenderのインターフェース

Blenderの操作画面は以下のような構成になっています。

 

1. トップバー
ファイルの保存や設定などのメニューが配置されています。

2. 3Dビュー
各種オブジェクトやカメラ、照明など立体空間上のモノが表示されます。

3. タイムライン
アニメーションの設定や編集に使用します。

4. アウトライナー
3Dビューに配置されている全てのモノが階層構造で表示されます。

5. プロパティ
3Dビューに配置されているモノの情報を表示したり、より細かな設定を行うことができます。

3Dビューで視点を操作する

Blenderでの作業では、主に3Dビューを使います。3Dビューの立体空間は、左右方向にX軸、前後方向にY軸、上下方向にZ軸となっており、さなざまな立体物を配置して作業することができます。3Dビューの左側にはツールバー、右上にはナビゲートが配置されています。

ナビゲートを使用することで、視点の変更や切り替えができます。
ナビゲート全体をドラッグするか、マウスの中ボタンを押しながら操作することで、配置したオブジェクトの周囲を見回すように視点を回転できます。また、ナビゲートのX(ーX)、Y(ーY)、Z(ーZ)ボタンをクリックすることで、それぞれの立体軸方向からの視点に切り替えることができます。
その他、Shiftキーとマウスの中ボタンを同時押しで操作すると視点の平行移動が、Controlキーとマウスの中ボタンを同時押しで操作すると視点の拡大縮小(ズームイン、ズームアウト)ができます。

オブジェクトの配置

オブジェクトモードにした状態で、追加メニューからメッシュを選択すると、様々なプリミティブ(基本的)なオブジェクトが選択できます。同様の操作は、ShiftキーとAキーの同時押しでも選択可能です。

配置したオブジェクトは、オブジェクトモードで、オブジェクトが選択された状態で、ツールバーの移動、回転、スケールボタンをクリックするか、キーボードのG(移動)、R(回転)、S(拡大縮小)キーを押すことで、配置や大きさなどを変更することができます。

オブジェクトの編集

3Dビュー左上のモード変更ボタンをクリックするか、キーボードのTabキーを押すことで、オブジェクトモードと編集モードの切り替えが可能です。編集モードに切り替えたら、オブジェクトの点、線、面を個別に編集することが可能です。

 

参考資料:M design 「Blenderの使い方・簡単なモデリングの解説」

練習課題:ここまでに学んだことを活用して、
「動物」のモデルを造り、完成させる。

2024プログラミング:Unityのプログラミング5

ゲームの実行ファイルの書き出し

完成したゲームを実行形式として構築することをビルドと呼称します。それでは、完成させたゲームをビルドしてみましょう。

Unityの画面上部のメニューからFile>Build Settingsを選択します。Build Settingsウィンドウが開いたら、Player Settingsをクリックします。Project Settingsウィンドウが開くのでPlayerの設定項目のResolution and PresentationのFullscreen Modeの選択項目からWindowedをクリックします。Default Screen Widthに1024、Default Screen Heightに600と入力しProject Settingsウィンドウを閉じます。

Build SettingsウィンドウでBuildをクリックします。実行ファイルの保存先を決めます。デスクトップなど任意の場所に先に保存先のフォルダーを新規作成しておくと良いでしょう。フォルダーの選択をクリックし、保存先を選択します。書き出しが始まるので、終了を待ちます。

書き出しが終わると、選択したフォルダーの中に複数のファイルが作成されています。その中から実行ファイル(ゲーム名.exe)を選択しダブルクリックするとゲームが始まります。

課題発表会
授業第15週に課題発表会を実施します。
完成したゲームを実行形式にし、作業用PCでゲームを遊べる状態で起動して
おき、授業に参加する学生全員で作業用PCを巡回しながらゲームを評価します。
そのため、ゲームは授業第14週までに完成させるようにし、実行形式にして
発表の準備をしておきましょう。
期末課題提出
ゲームを実行ファイルに変換して、完成したフォルダを圧縮し、ZIP形式にして
CSの授業掲示板、プログラミングⅠ「期末課題提出」へ提出してください。

圧縮したフォルダが50MBを超えた場合はTeamsのプログラミングⅠ授業チームに
アップロードしてください。その際、アップロードしたことを、以下の
メールアドレスに連絡してください。

taisum@asu.aasa.ac.jp

締切日 2024年8月8日(木)まで
(同日の日付を超えた場合はいかなる理由があっても受け取りませんので、
余裕をもって取り組んでください。)

2021プログラミング6:サウンドのビジュアライズ

音量の視覚化(再掲)

サウンドの音量(ボリューム)とグラフィックスを関連づけてみましょう。

let sound, analyzer;

function preload() {
 sound = loadSound('test.mp3');
}

function setup() {
  createCanvas(400, 200);
  analyzer = new p5.Amplitude();
  sound.pause();
  sound.loop();
  analyzer.setInput(sound);
}

function draw() {
    background(0);
    let rms = analyzer.getLevel();
    fill(255,0,0);
    ellipse(width/2, height/2, 10+rms*200, 10+rms*200);
}
総合課題2:
上記のサウンド再生ソフトを改良し独自のデザインにする。
ただし以下の機能は全て必ず実装すること。
画面のサイズや画像の配置は自由とする。
・サウンドデータを3種類以上使用する。
・外部から取得した画像(JPEGデータや、PNGデータ)を1種類以上(背景含む)使用する。
・サウンドの音量で独自に用意した2種類以上の画像を変形させる。
・背景画像を用意し配置する。

課題発表:12月16日(木)授業内で発表してもらいます

 

2021プログラミング5:お絵かきアプリのインターフェース

インターフェイスデザイン

インターフェース(interface)とは英語で境界や界面を表す言葉です。この講座では、ユーザーインターフェースと呼ばれる、人間と機械の境界を取り扱います。お絵かきソフトのインターフェースデザインを通して、プログラミングの過程を学んでいきましょう。

まず、はじめに作成するお絵かきソフトの画面サイズを決めます。ここでは400ピクセルx400ピクセルの画面で作成することにします。また背景色を白に設定します。

function setup(){
  createCanvas(400,400);
 background(255);
}

function draw(){
  
}

次に、幅20ピクセル・高さ20ピクセルの黒色の円がマウスの動きに付いてくるようにします。

let penX = 0;//円のX座標
let penY = 0;//円のY座標
let penSize = 20;//円の直径

function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;//円のX座標にマウスのX座標を代入
  penY = mouseY;//円のY座標にマウスのY座標を代入
  fill(0);//円の塗りを黒色にする
  noStroke();//円の輪郭線の描画を無効にする
  ellipse(penX,penY,penSize,penSize);//円の描画
}

円のサイズを変えるためのアイコンを配置します。アイコンが描線で隠れてしまわないようにアイコンの背景として長方形を配置します。

let penX = 0;
let penY = 0;
let penSize = 20;

function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  fill(0);
  noStroke();
  ellipse(penX,penY,penSize,penSize);
  fill(255);
  rect(0,300,400,100);//アイコンの背景
  fill(0);
  ellipse(50,350,20,20);//細い描線のアイコン
  ellipse(100,350,40,40);//太い描線のアイコン 
}

アイコンの上にマウスが重なると描線の太さが変わるように設定します。

let penX = 0;
let penY = 0;
let penSize = 20;

function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  fill(0);
  noStroke();
  ellipse(penX,penY,penSize,penSize);
  fill(255);
  rect(0,300,400,100);
  fill(0);
  ellipse(50,350,20,20);
  ellipse(100,350,40,40); 
  if(penX<60 && penY>340){ //小さい円のアイコンの座標にマウスが重なった場合
    penSize=20;
  }
  if(penX>60 && penX<120 && penY>340){ //太い円のアイコンの座標にマウスが重なった場合
    penSize=40;
  }
}

同じ手法を使って描線の色を変えてみましょう。

let penX = 0;
let penY = 0;
let penSize = 20;
let penR = 0;//描線の色・赤
let penG = 0;//描線の色・緑
let penB = 0;//描線の色・青


function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  fill(penR,penG,penB);
  noStroke();
  ellipse(penX,penY,penSize,penSize);
  fill(255);
  rect(0,300,400,100);
  fill(0);
  ellipse(50,350,20,20);
  ellipse(100,350,40,40);
  fill(255,0,0);
  ellipse(150,350,40,40);
  fill(0,255,0);
  ellipse(200,350,40,40);
  fill(0,0,255);
  ellipse(250,350,40,40);
  if(penX<60 && penY>340){
    penSize=20;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>60 && penX<120 && penY>340){
    penSize=40;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>120 && penX<170 && penY>340){ //アイコンに描線が重なったら描線の色を赤色に変更する
    penR = 255;
    penG = 0;
    penB = 0;
  }
  if(penX>170 && penX<220 && penY>340){ //アイコンに描線が重なったら描線の色を緑色に変更する
    penR = 0;
    penG = 255;
    penB = 0;
  }
  if(penX>220 && penX<270 && penY>340){ //アイコンに描線が重なったら描線の色を青色に変更する
    penR = 0;
    penG = 0;
    penB = 255;
  }
}

更に描線をline()関数を使用してスムーズにつなげてみましょう。1フレーム前のX座標、Y座標を格納するpmouseX、pmouseYという変数を使用します。

let penX = 0;
let penY = 0;
let penPX = 0;//pmouseXの値を格納する変数
let penPY = 0;//pmouseYの値を格納する変数
let penSize = 20;
let penR = 0;
let penG = 0;
let penB = 0;


function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  penPX = pmouseX; //1フレーム前のX座標を代入
  penPY = pmouseY; //1フレーム前のY座標を代入
  stroke(penR,penG,penB); //線の色を設定
  strokeWeight(penSize); //線の太さを設定
  line(penX,penY,penPX,penPY); //線を描画
  noStroke(); //以下のオブジェには線を描画しないよう設定
  fill(255);
  rect(0,300,400,100);
  fill(0);
  ellipse(50,350,20,20);
  ellipse(100,350,40,40);
  fill(255,0,0);
  ellipse(150,350,40,40);
  fill(0,255,0);
  ellipse(200,350,40,40);
  fill(0,0,255);
  ellipse(250,350,40,40);
  if(penX<60 && penY>340){
    penSize=20;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>60 && penX<120 && penY>340){
    penSize=40;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>120 && penX<170 && penY>340){
    penR = 255;
    penG = 0;
    penB = 0;
  }
  if(penX>170 && penX<220 && penY>340){
    penR = 0;
    penG = 255;
    penB = 0;
  }
  if(penX>220 && penX<270 && penY>340){
    penR = 0;
    penG = 0;
    penB = 255;
  }
}

次にマウスボタンが押されているときだけ、描線が描画されるように改良しましょう。

let penX = 0;
let penY = 0;
let penPX = 0;
let penPY = 0;
let penSize = 20;
let penR = 0;
let penG = 0;
let penB = 0;


function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  penPX = pmouseX;
  penPY = pmouseY;
  stroke(penR,penG,penB);
  strokeWeight(penSize);
  if(mouseIsPressed){ //マウスボタンが押されている間だけコードが実行される
    line(penX,penY,penPX,penPY);
  }
  noStroke();
  fill(255);
  rect(0,300,400,100);
  fill(0);
  ellipse(50,350,20,20);
  ellipse(100,350,40,40);
  fill(255,0,0);
  ellipse(150,350,40,40);
  fill(0,255,0);
  ellipse(200,350,40,40);
  fill(0,0,255);
  ellipse(250,350,40,40);
  if(penX<60 && penY>340){
    penSize=20;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>60 && penX<120 && penY>340){
    penSize=40;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>120 && penX<170 && penY>340){
    penR = 255;
    penG = 0;
    penB = 0;
  }
  if(penX>170 && penX<220 && penY>340){
    penR = 0;
    penG = 255;
    penB = 0;
  }
  if(penX>220 && penX<270 && penY>340){
    penR = 0;
    penG = 0;
    penB = 255;
  }
}

最後に、キーボードの特定のボタンを押すと描線がリセットされるようにしましょう。isKeyPressedおよびkeyという関数を用いることで、キーボードが押されているかどうか、また、どのキーが押されているかをif文で判定するコードを書き加えます。以下のコードでは、キーボードのDボタンが押されると、画面をリセット(白色の四角形を最前面に描画)するようにしています。

let penX = 0;
let penY = 0;
let penPX = 0;
let penPY = 0;
let penSize = 20;
let penR = 0;
let penG = 0;
let penB = 0;

function setup(){
  createCanvas(400,400);
  background(255);
}

function draw(){
  penX = mouseX;
  penY = mouseY;
  penPX = pmouseX;
  penPY = pmouseY;
  stroke(penR,penG,penB);
  strokeWeight(penSize);
  if(mouseIsPressed){
    line(penX,penY,penPX,penPY);
  }
  noStroke();
  fill(255);
  rect(0,300,400,100);
  fill(0);
  ellipse(50,350,20,20);
  ellipse(100,350,40,40);
  fill(255,0,0);
  ellipse(150,350,40,40);
  fill(0,255,0);
  ellipse(200,350,40,40);
  fill(0,0,255);
  ellipse(250,350,40,40);
  if(penX<60 && penY>340){
    penSize=20;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>60 && penX<120 && penY>340){
    penSize=40;
    penR = 0;
    penG = 0;
    penB = 0;
  }
  if(penX>120 && penX<170 && penY>340){
    penR = 255;
    penG = 0;
    penB = 0;
  }
  if(penX>170 && penX<220 && penY>340){
    penR = 0;
    penG = 255;
    penB = 0;
  }
  if(penX>220 && penX<270 && penY>340){
    penR = 0;
    penG = 0;
    penB = 255;
  }
  if ((isKeyPressed == true) && ((key == 'd') ||  (key == 'D'))) {
    fill(255);
    noStroke();
    rect(0,0,400,300);
  }
}
課題
上記のお絵かきアプリを改良し独自のインターフェースデザインにリデザインする。 
ただし以下の機能は全て必ず実装すること。画面のサイズやアイコンの配置は自由とする。
・3段階以上の太さの描線で描画できるようにする。
・4色以上の色で描画できるようにする。
・消しゴム機能を追加する。(ヒント:白色の描線を描くことで代用できる)
その他、余裕のある学生は画像をアイコンにして、画像を円の代わりに配置し、
画像で絵を描く機能を実装するなどしても構わない。音声を追加するなどしても良い。

締め切り:
11月25日(木)の授業内で講評会を実施します。