リブlab

理系大学生のIT・電気・大学・趣味についての日記

Unityの罠をQ&A形式で解決してしんぜよう01

 今度、今回の記事の内容を分割して記事にします。タイトルだけだと欲しい情報見つけにくいと思うので。

目次

 Q.ifの条件にGetAxisを使いたいけどcastしても通用しない!どうしよう!

 A.if(Input.GetAxis("Horizontal") != 0) { //do something }のようにしましょう。

 Q.ゲーム(Scene)を再生すると勝手に音が出るんすけど。

 A.AudioSourceのPlayOnAwakeを確認しよう!チェックがついているはずだ!

 Q.それでもだめです!

 A.なにー!それならこのようなコードを書け!

    AudioSource mysound;
    bool toggle = false;

   void Update()
    {
  if(!toggle)
       Invoke("toggleChange", 0.1f);
  if(toggle)
       {
            if (!(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A)
                || Input.GetKey(KeyCode.S)  || Input.GetKey(KeyCode.D)))
            {
                mysound.Play();
            }
       }
    }
    //副作用マシマシな関数ですまない
    void toggleChange()
    {
        toggle = true;
    }

 このスクリプトのようにtoggleをフラッグにして判定すればいいぞ!こうすることで一回だけInvokeすることになる!

 今回の場合だと、WASDキーの内どれかを押している間は音が出て、押していないときは音が出ないぞ!勿論、開始時に音は出ない!!!(何故これでうまくいくのか分かってないがうまくいった。(なんでNOT演算子がいるんですかね))

 ちなみに、これは開始時に音を出さないようにInvokeによりn秒後に処理を実行するようにしている。今回の処理はtoggleをfalseからtrueに変更した。Invokeは処理を遅らせて実行するのに使われているぞ。
 処理速度的にifの入れ子はどうかと思うがしゃーない。

 Q.ポーズ画面をTime.timeScaleで実装したら音が鳴り続けるぞゴラァ

 A.申し訳ございませんお客様。今回の場合ですとこのように実装なさってはいかがでしょうか。

    // Update is called once per frame
    void Update()
    {
        if (ポーズが呼ばれたとき)
        {
            if (ポーズUIがインスタンス化されていない時)
            {
    ポーズUIをインスタンス化
                Time.timeScale = 0f;
                AudioListener.volume = 0;
            }
            else
            {
                Destroy(ポーズUI);
                Time.timeScale = 1f;
                Invoke("AudioChange", 0.1f);
                
            }
        }
    }
    void AudioChange()
    {
        AudioListener.volume = 1;
    }

 のように実装なさってください。使いやすいように不必要な情報を省き、抽象化しております。Pause画面の作り方をご存じなければ、下記の記事をご参考ください。

gametukurikata.com

  では、今回のスクリプトのご説明をさせていただきます。AudioListner.volumeの値を変えることでミュート機能を実装しました。ポーズ時にBGMをおかけになる場合には使えませんのでご注意ください。
 AudioListner.volumeの値をいじることで聞こえる音量を調節できます。
値の範囲は0.0~1.0です。0の時に聞こえません。
ちなみにAudioListner.pauseというものもございます。
 Invokeにつきましては、ゲーム再開後に謎の音声が出ますのでその対策に記述していますことをご留意ください。

参考リンク

AudioListener.volume - Unity スクリプトリファレンス

AudioListener.pause - Unity スクリプトリファレンス

 Q.ポーズUIのつくり方のリンクの通りにUpdate関数に下記のものを実装しましたがUpdate関数が止まりません。
   void Update()
    {
        //これでpauseしてる時にUpdateを止める。しかし、音はなる。しかし、音以外の処理はうまくいく。
        if (Mathf.Approximately(Time.timeScale, 0f))
        {
            return;
        }
     }

 A.仕様かバグです。諦めて他の手を考えましょう。なので一つ上のやり方で実践してください。

さいごに

 今回の記事はすべて自分の経験をもとに書いています。なぜPlayOnAwakeのチェックを外してもゲーム開始時に音がでるのかとか、それ対策のコードで何故解決できたのかとかは一応理由は予測できてますが

if (!(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) 
  || Input.GetKey(KeyCode.S)  || Input.GetKey(KeyCode.D)))

 これについては全く意味が分かりませんが、これからも精進したいと思います。 その過程で得たものなどは備忘録がてら日記を書くことで、皆さんと共有したいと思います。
 コメントやマサカリ歓迎です。お待ちしています。ご閲覧ありがとうございました。

リブの競プロテンプレート(随時更新)説明あり

コメント付きです。そのうちコメントじゃなくて見やすい解説を作ろうかな。

#include <bits/stdc++.h>
using namespace std;   //開発で使うべからず

/* REPmacro */
//一個下は#define rep(i,n) for(int i=0;i<n;i++)の改良版
#define REP(i,n) for(int i=0,i##_len=(n);i<i##_len;++i)
#define REPR(i,n) for(int i=((int)(n)-1);i>=0;--i)
#define FOR(i,m,n) for(int i=(int)(m);i<(int)(n);++i)
/*
上の説明
   REP(i,10){...} //i=0~i=9までのループ
   FOR(i,3,10){...} //i=3~i=9までのループ
   REPR(i,10){...} //i=9~i=0までのループ
*/

/*alias*/
/*ALLはコンテナaが2回別々に評価される。 たとえばsort(ALL(field[y++]))のようなことをするとバグになるらしい。なので、気を付けて使うつもり。*/
#define ALL(x) (x).begin(),(x).end()
//size()のunsignedをintへ
#define SZ(x) ((int)(x).size())
#define F first
#define S second
#define PB push_back
#define EB emplace_back
#define MP make_pair
//#define cauto const auto
//#define caa const auto&   const auto ampersand

//y0は標準ライブラリ関数らしい。ので、事故防止
#define y0 y3487465
#define y1 y8687969
#define j0 j1347829
#define j1 j234892
#define next asdnext
#define prev asdprev
#define itn int

/*using*/
//using lnt = long long;  l(小文字のエル)
//using unsi = unsigned;  アンジーで覚えてる

using ll = long long;   //(小文字のエル)
using ull = unsigned long long;
using unsi = unsigned;
using vi = vector<int>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using pii = pair<int, int>;
//doubleは1e+15~1e-15ぐらい.floatは1e+6~1e-6ぐらいの精度
using db = double;
using plex = complex<D>;
using vs = vector<string>;
/*した二つはエラーになるので使うならdefine*/
//using cauto = const auto;
//using caa = const auto&;

/*template*/
//標準のmaxは比べるだけだけどこれは代入(assign)してくれる
template<class T>inline bool amax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>inline bool amin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

/* func */

/*struct*/
struct aaa{
    aaa(){
        cin.tie(0); ios::sync_with_stdio(0); cout<<fixed<<setprecision(20);
    };
}aaaaaaa;

//上の簡易版struct Fast {Fast(){std::cin.tie(0);ios::sync_with_stdio(false);}} fast;

/* const */
//const int INF = 1001001001;
//const ll LINF = 1001001001001001001ll;
//const int MOD = 1e9 + 7; //10^9
//const db EPS = 1e-9;
//上のやつらをconst autoにするのか問題
//加えてconstexprにするのか問題
const int dx[] = { 1, 1, 0, -1, -1, -1, 0, 1 }, dy[] = { 0, 1, 1, 1, 0, -1, -1, -1 };  //Counterclockwise from the 6o'clock direction
//Take the x-axis downward and the y-axis right x軸を下、y軸を右

//#define int ll
//#define int long long
//これらはコメントアウトを消すことでsignedの力が解放する

signed main()
{
/*
構造体aaaでmainの冒頭で書く処理を書いてmainをすっきりさせる。
   cin.tie(0);
   ios::sync_with_stdio(false);
(cin,cout)か(printf,scanf)の組のどちらかに使用を限定
*/




  return 0;
}

/*
memo

・打ち間違いでもセーフというやつ。
#define itn int

・sign main()を見かけたけど使い道がよく分からん。
と思ってたけど long long を int にdefineする宗派がmain()を使うとエラーになるかららしい。
*/

リブの競プロテンプレート(随時更新)説明なし

 簡易版テンプレと普通のテンプレ書きました。説明付きのテンプレは次回載せます。

#include <bits/stdc++.h>
using namespace std;

/* REPmacro */
#define REP(i,n) for(int i=0,i##_len=(n);i<i##_len;++i)
#define REPR(i,n) for(int i=((int)(n)-1);i>=0;--i)
#define FOR(i,m,n) for(int i=(int)(m);i<(int)(n);++i)

/*alias*/
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define F first
#define S second
#define PB push_back
#define EB emplace_back
#define MP make_pair 

#define y0 y3487465
#define y1 y8687969
#define j0 j1347829
#define j1 j234892
#define next asdnext
#define prev asdprev

/*template*/
template<class T>inline bool amax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>inline bool amin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

/*struct*/
struct aaa{
    aaa(){
        cin.tie(0); ios::sync_with_stdio(0); cout<<fixed<<setprecision(20);
    };
}aaaaaaa;


/* const */
const int INF = 1001001001;
const long long LINF = 1001001001001001001ll;
const int MOD = 1e9 + 7; //10^9
const double EPS = 1e-9;

//#define int long long

signed main()
{

  return 0;
}

普通

#include <bits/stdc++.h>
using namespace std;

/* REPmacro */
#define REP(i,n) for(int i=0,i##_len=(n);i<i##_len;++i)
#define REPR(i,n) for(int i=((int)(n)-1);i>=0;--i)
#define FOR(i,m,n) for(int i=(int)(m);i<(int)(n);++i)

/*alias*/
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define F first
#define S second
#define PB push_back
#define EB emplace_back
#define MP make_pair 

#define y0 y3487465
#define y1 y8687969
#define j0 j1347829
#define j1 j234892
#define next asdnext
#define prev asdprev
#define itn int

/*using*/
using ll = long long;
using ull = unsigned long long;
using unsi = unsigned;
using vi = vector<int>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using pii = pair<int, int>;
using db = double;
using plex = complex<double>;
using vs = vector<string>;

/*template*/
template<class T>inline bool amax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>inline bool amin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }

/*struct*/
struct aaa{
    aaa(){
        cin.tie(0); ios::sync_with_stdio(0); cout<<fixed<<setprecision(20);
    };
}aaaaaaa;

/* const */
const int INF = 1001001001;
const ll LINF = 1001001001001001001ll;
const int MOD = 1e9 + 7; //10^9
const db EPS = 1e-9;
const int dx[] = { 1, 1, 0, -1, -1, -1, 0, 1 }, dy[] = { 0, 1, 1, 1, 0, -1, -1, -1 };  //Counterclockwise from the 6o'clock direction
//Take the x-axis downward and the y-axis right

//#define int ll
//#define int long long

signed main()
{

  return 0;
}

スマホ用とPC用で見やすさが違うので対策

 このブログをスマホで見ると死ぬほど見にくいので、スマホ用とPC用で二つ記事を作ることにしました。
 過去記事についてはぼちぼち作り直していきます。

更新1
 結局、二つ作ってない。

AtCoderの良解答からみるプログラミングのテクニック01(ビギナー向け) C++

 最近競技プログラミングに興味を持ち始めてみました。
ここから、学んだことを共有したいと思います。(ビギナー向け)
 途中に他の方のコードを引用しますが、そのコードの著作権はそのコードを書いた方が有しています。
 元のコードが見たい場合は問題名 提出#〇〇〇〇〇で検索をすれば見ることができます。
 今回から第九回までは提出#〇〇〇〇〇のかたちで紹介させていただきます。が十回目からはリンクを張ることにしました。検索してもでず、地道に番号から探さないと見つけられないみたいだからです。

※注 スマホだとコードは見にくいのでPCモードかPCで見たほうが良いです。

 

AtCorderとは

 AtCorderが何なのかは

https://qiita.com/drken/items/fd4e5e3630d0f5859067

 ここに全て(始め方も)が詰まってます。この記事に沿って僕の記事も進めていきます。
 なので、詰まった時や、他の人の解答を見たいけど似たような解答が多くて探すのが大変って時に見に来るのがいいかもしれません。

At Corder で見つけた良解答から得たもの

 ABC064_A

 三つの整数が与えられ、左から整数を読んだときにこれが 4の倍数であるか判定しなさい。という問題です。

 テク1 - 条件演算子の効果と倍数判定のコツ

 提出 #4187968の中に

((g*10+b)%4)? "NO":"YES")

 というコードがあります。僕は

#include<iostream>
using namespace std;
int main()
{
  int valuePlace100, valuePlace10, valuePlace1;
  cin >> valuePlace100 >> valuePlace10 >> valuePlace1;
  int sum = (valuePlace100)*100+(valuePlace10)*10+valuePlace1;
  if(sum % 4 == 0)
  cout << "YES";
  else
  cout << "NO";
  return 0;
}

 という、あまり何も考えてない(命名だけしっかりしてる。(つもり))コードを書きました。

 僕のは、与えられた整数を左から100倍,10倍してそれらを足すことで三桁の整数を作りました。そして、それを倍数判定の基本、「余りを求める」を利用して解きました。

 別のやり方として、stringで整数をまとめてからint型に変換するというやり方もあります。

 で、ご紹介した解法ですが、条件演算子を使って可読性が高くなっています。
さらに(g*10+b)%4によって、今回の場合だと百の位は倍数判定において考慮しなくていいことが分かります。

 Tips01

 競プロにおいて役に立つ#include <bits/stdc++.h>という記述があります。
これによって、C++の標準ライブラリを一行で全てインクルードすることができます。


 ABC082_A

 二整数が与えられ、それらの平均を少数点切り上げで出力せよという問題

 テク2 - 小数点切り上げを簡単に
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int a,b;
  
  cin >> a;
  cin >> b;
  
  if (0 == (a + b) % 2) {
    cout << (a + b) / 2 << endl;
  } else {
    cout << (a + b) / 2 + 1 << endl;
  }
  

 個人的におすすめはしたくない解答です。なぜなら、長い。それに尽きますね。
見たら分かりますが、2で割り切れなかったら1を足して平均をとるというコードです。

 今回は、こちらのコードがすっきりしていると思います。

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
  int a, b;
  cin >> a >> b;
  int c = a + b;
  int result = (c + 1)/2;
  cout << result << endl;
  
  return 0;
}

 はい、このようにわざわざ場合分けしなくても一発で出ます。
これは与えられた数がn整数の場合だと、(平均値) =((全体の和) + (n - 1)) / n と書けます。
 このように記述することで簡潔に平均値を切り上げるコードが書けます。どうしてこのように書けるのかは、分配法則を適用して値をいくつか代入してみると分かるのではないでしょうか。

 Tips02

 #defineを使うことで記述を簡単にする。
例)#define R cin>>

 Tips03

 templateや関数を作っておく。

 Caution01

 YesやNoを出力するとき、YESだったりNOだったりしてWAになることがあるので気を付けましょう。(血涙)

おわり

 以上、ABC 064とABC 082の解説でしたー。

次回はここをクリック

個人的メモ2

・toggle: 同じ操作で二つの状態に交互に切り替わること

・JCT(Japanese consumption tax)= 消費税

・VAT(value-added tax)= 付加価値税

プリプロセッサで#の行が処理されるよ

エスケープシーケンスのベルはwindows10おなじみのテレレーンって鳴るよ。vs_installer開けたら鳴るやつね。

・フォームフィードまたは改ページって言われる¥fはプリンターやコピー機に印刷することらしい。らしい。

・水平タブ(¥t)は、タブを入れて一つ右の区切りに送る。垂直タブ(¥V)は、一つ下の行に移る。

・¥rは復帰(行の先頭に戻る)という意味の制御コード。 16進数では「0x0D」 アスキーコード(いわゆる半角文字)の空白(0x20)未満のコードは、制御コードに割り当てられている。

・¥ ≒ \ はわかるよね?打つときは普通¥だけどlinuxとか \ これになる。visual stdioでバックスラッシュでも打ってみたら¥になって改行された。¥= \ でええのか?

・strcpyが使えなかったら_sを後ろにつけよう

・Cの構造体はメンバ関数は無いぞ。C++の構造体とクラスの違いは前回のメモに書いた通り。

c++)・cin << ○○;とするとストリームの中に入力したリテラルを記録することができる。
enter押すまでずっと、記録できる。
△△(enter)と入力するとそのリテラルが一個の入力して保存される。
△△(スペース)△△(enter)と入力するとリテラルが二つストリームに保存される。

 気を付けないといけないのは、例えcin << a;としてもストリームにはenterキーを押すまでは、入力が可能であること。よって、cin << a;と書いて△△(スペース)□□(enter)とするとストリームには□□が残る。

 なので、ユーザーが間違えて一個多く入力してしまうとストリームには不必要なデータが残る。
よって、予期せぬ値を出力することになる。

int main()
{
    int a, b, c;

 cin >> a >> b;
 cout << a << b;
 cout << a << b;

 cout << "cに値を記録します。";
 while (cin >> c)
    cout << c;
}

このプログラムを、一度入力した後に二回出力しもう一度入力をするというつもりで書く人がいると思います。
これに対して一回目の入力を

1 2 3 4 5

とします。(3,4,5を二回目の入力で打つつもりだったが間違えて一回目の入力で打ち込んだとする。)しかし、二回目の入力は現れず、出力されます。

1 2 1 2 cに値を代入します3 4 5

となります。1212はa,bに入力していたため予期していた通りに動いています。
しかし、3,4,5は初めの入力しか行っていないのにcに入力されることになります。

 では、どうするのか?
cin.ignore()を使いましょう。使用例はここにあります。↓

stackoverflow.com

 ここに使い方まで丁寧に書いてあるので、ここを足掛かりにcin.ignore()への理解を深めましょう。

自分の過去記事を振り返って反省したことを共有したい

 こんにちは、リブです。
時間ができたのでブログを再開したいと思い、この前自分の記事を見返しました。
見やすさであったり、内容であったり、改善点は色々見つかりましたが、特に気になったものをご紹介したいと思います。

反省1
 まず、はじめに対象読者のブレを感じたことです。完全プログラミング入門者に向けているのか、他のエディタは使ったことがあるけどVisual Studioは初めてですという人に向けているのかがあいまいだと感じました。

対策1
 そのため、次回からは記事の初めに「対象読者としてこういう人を想定して書きました」ということを記載したいと思います。もちろん、対象から外れていてもある程度は身になるはずです。

反省2
 どういう目的の記事なのかがよく分からない。ひとつ前の記事を見たら分かると思うのですが、使い方を教えたいのか、画面の説明をしたいのかが明確ではないと思いました。

hajimekata-nyumon-donyu.hatenablog.com

 なので、書き直したいと思います。今後の方針としては、実際に手を動かしながらVSの使い方を学びます。そして、「visual studioの画面にあるこれは何なのか」というリファレンスみたいなものをちゃちゃっと作り直したいと思います。

 しかし、リファレンスが先になるかもしれません。なぜなら、現在「手を動かすのは動画で説明したほうが分かりやすいのでは?」という考えが頭にあるからです。

記事の書き方の構想
 現在、対話形式で進めていく感じにしようか悩んでいます。
理由は対話形式のほうが分かりやすく感じるからです。どういうところが分かりやすいかというと、つまずきやすいところの説明がしやすいところだと思います。
 例えば、キャラAとBがいたとします。Aが講師役でBが生徒役です。数学の不定積分についての例だと。

A「このような計算をすることを不定積分と呼びます。試しにB君この問題を解いてみましょう」
B「はい」
ー少ししてー
B「解き終わりました!」
A「どれどれ、うーん。惜しい!積分定数を忘れているね。」
B「あ、忘れてた。テストの時には気を付けないと」

のようにしたほうが気を付けるべきことが残りやすい。または、より明確になる気がします。個人的に。
 これを普通に書くと、「積分定数不定積分を行ったときに忘れないようにしましょう」となり、スーと頭を通り抜けそうです。
 さらに、

B「なるほど、積分というのは仕事を求めるときにも使えるんだな。物理と数学は密接な関係にあるのか。」

のように、生徒役がある事象に対してどうして納得したのかを書くと読み手の理解が捗ると思いました。

 もしかしたら、動画ではなくこのような対話形式で手を動かしていく記事にする可能性が考えられます。

終わりに

 今回の記事はいかがだったでしょうか。夏から冬にかけての大学生活の中で、人に伝えるという技術が少し上がった感じが見受けられる記事になったと個人的に思ってます。(笑)

終わりに2

 この記事を書いてからもう一度過去記事を見返したら、そこまで悪くない気がしてきた。。。
自分は「Visual Studioのこのメニューにはこういう機能がありますよって説明したかっただけ」ということを分かっているからかもしれませんが。。。
 というより、一番初めの記事から追っているなら分かる。けど、途中から見ると少しわかりにくい構成だと気づいた。
 なので、要望があれば書き直します。(最終更新時の発言)