【Unity】万年カレンダーを作る

2019/07/25更新
2021/02/15修正・カレンダーの新記事を上げています。
【Unity】カレンダーを作り直してみた併せてご覧ください。


今回はタイトルから分かる通りカレンダーを作るです。

なんでUnityで?
と思う方がほとんどでしょう。

実は先日、嫁さんと話してた時に

「放送カレンダー作れんの?」
ケロ「Unityってゲーム作るツールやからね。」
「そうなんや?プログラムやから作れるかと思った。」

こんな会話なんですが、
ケロ夫婦は野球好きでプロ野球をよく見ます。
スカパーの放送日程をアプリで見れると便利なんだけど…

なんて話になりました。

「無理ならいいわ」
と言われてしまったんですが、
Unityでも作れない事はないんで、勉強がてら作ってみる事に。
少し、無理って言葉にムキになった部分もあります (*¨) ポッ・・・


調べてみると

チワ裏さんの
Unityでカレンダー作ったメモ
がヒットしました。

導入にはLinqやuniRxの知識が要りそうなので、
少し敷居が高そうです。

まだスクリプトを書き始めたばかりなので、
LinqやuniRxの勉強は頭が付いて行きません(@@;;;) はて?

いずれは勉強しないとダメなんでしょうが、
説明を見ても頭に入らないと言うか理解が…
頭の老化が激しい今日この頃(..*) オハズカシイ・・


今の知識で考えられる方法はないかと思って
検索を進めると…


DateTime構造体と言うのがあるそうですね~
現在の日付や時間を取得する為の物だそうです。
C#にはいろいろ便利な物があるのですが、
複雑で知らない事だらけでっす。(@@;;;)

便利に使える物を作ってる方がいると認識しておきます。(~-~;)ヾ(-_-;) オイオイ


では早速スクリプトを少々
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class CalendarManager : MonoBehaviour {

// Use this for initialization
void Start () {

DateTime now = DateTime.Now;
int year = now.Year; // 年
int month = now.Month; // 月
int day = now.Day; // 日

Debug.Log(year+"/"+month+"/"+day);
}

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

}
}
DateTimeを使う時は、
名前空間にSystemを宣言する必要があるそうです。

これでDateTimeから現在日がログに出力されます。
便利ですね~ (´ー`) ウフフ


さて、現在日の割り出しが分かったので、
ここから当月のカレンダーを自動作成されるように組まないと
いけないのですが、はてさてどうした物か…

月初が何曜日から?が分からないと、
1日を割り当てる事もできないので、
月初の曜日を割り出してみます。

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

public class CalendarManager : MonoBehaviour {

// Use this for initialization
void Start () {

DateTime now = DateTime.Now;
int year = now.Year; // 年
int month = now.Month; // 月
int day = now.Day;

DateTime firstDay = new DateTime(year, month, 1);
DayOfWeek firstDate = firstDay.DayOfWeek;

Debug.Log(firstDate);
}

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

}
}
月初の曜日は DayOfWeek で、
割り出す事ができるようです。
変数firstDateに曜日が格納されました。


ここからはカレンダーの作成ですが、
万年カレンダーとなると5~6週の枠が必要になります。
適当に7×6マスのカレンダー枠を作ってUnityにインポートします。

Calendar1.jpg
1日が土曜から始まると最大で37枠まで必要なので、
6週目の2枠まで、これで最大日数から最小日数まで対応できそうです。

枠の上に年・月のテキストを用意して、枠のサイズを調整します。
Calendar2.jpg
年月のテキストは4個あるので、Yearsparentの空オブジェクトを用意して、
その中に子にして入れています。

TextYearとTextMonthをスクリプトから書き替えるようにするので、
空白にしておきます。

続いて、Dayテキストを37個用意します。
Calendar3.jpg
Dayparentの空オブジェクトを用意して、
その中に子のText0~36を入れておきます。
この時、左縦列の文字カラーを赤、右縦列の文字カラーを青にしておくと
日曜と土曜が色分けされて見やすいかと思います。
ここまで出来たらText0~36を空白にしておきます。

Calendar4.jpg
書き替えるTextを空白にするとこんな感じになるかと思います。


ここからはスクリプトになります。

37枠分のループを回して、
開始曜日から1日になるようにTextの書き替えを行っています。

public GameObject[] DayText; //日付テキスト
private int days; //テキストに代入する日付

// Use this for initialization
void Start () {
switch (firstDate)
{
case DayOfWeek.Sunday:
for (int i = 0; i <= 36; i++)
{
days = i + 1;
DayText[i].GetComponent<Text>().text = days.ToString();
}
break;

case DayOfWeek.Monday:
for (int i = 0; i <= 36; i++)
{
if (i >= 1 )
{
DayText[i].GetComponent<Text>().text = i.ToString();

}
}
break;

case DayOfWeek.Tuesday:
for (int i = 0; i <= 36; i++)
{
days = i - 1;
if (i >= 2 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
}
break;

case DayOfWeek.Wednesday:
for (int i = 0; i <= 36; i++)
{
days = i - 2;
if (i >= 3 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
}
break;

case DayOfWeek.Thursday:
for (int i = 0; i <= 36; i++)
{
days = i - 3;
if (i >= 4 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
}
break;

case DayOfWeek.Friday:
for (int i = 0; i <= 36; i++)
{
days = i - 4;
if (i >= 5 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
}
break;

case DayOfWeek.Saturday:
for (int i = 0; i <= 36; i++)
{
days = i - 5;
if (i >= 6 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
}
break;
}
}
分かりにくいですね。

説明すると
Textの書き替えを37個分行っています。

ループ数から日付を計算しているので。
日曜日からスタートだとText0の内容を1にする必要があります。
ループは0からスタートするので+1で補正しています。

月曜ならループの数値をそのまま代入できますが、
左上に0が代入されてしまいます。
なのでif文で1以上ならText代入としています。

火曜日以降は、枠番の数値から日付を割り出して、
if文で開始枠を判断しています。
Calendar6.jpg
日曜開始ならループiは0なので+1
月曜開始ならループiは1なので補正なし
火曜開始ならループiは2なので-1
水曜以降は-2、-3、と減算しています。


これで日付の計算ができたのでプレイすると
31・32・33… (TT; )( ;TT) オロオロ

上限の日付を設定していないので、最後まで書き替えされてる~
月の最終日を調べる必要があります。

月の最終日は DateTime.DaysInMonth(年、月) で、
取得できるようです。

ループiの数値を引用して補正を掛けて、
最終的にできたのがこちら
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;

public class CalendarManager : MonoBehaviour {

DateTime now; //現在日
DayOfWeek firstDate; //月初の曜日

public GameObject YearText; //年テキスト
public GameObject MonthText; //月テキスト
public GameObject[] DayText; //日付テキスト

private int year; //年
private int month; //月
private int day; //日

private int days; //テキストに代入する日付


// Use this for initialization
void Start () {
SearchYears();
RewriteText();
}

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

}


//現在日から月初の曜日を割り出す
void SearchYears()
{
now = DateTime.Now;
year = now.Year; //現年度
month = now.Month; //現月度
day = now.Day; //現日付

DateTime firstDay = new DateTime(year,month , 1);
firstDate = firstDay.DayOfWeek;
}


//年・月・日のテキストを書き替える
void RewriteText()
{
YearText.GetComponent<Text>().text = year.ToString(); //年の書き替え
MonthText.GetComponent<Text>().text = month.ToString(); //月の書き替え

int monthEnd = DateTime.DaysInMonth(year, month); //月の最終日を取得


/* ・月初の曜日から枠の日付を書き替える
・次月の日付は空白にする */

switch (firstDate)
{
case DayOfWeek.Sunday:
for (int i = 0; i <= 36; i++)
{
days = i + 1;
if (monthEnd >= days)
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Monday:
for (int i = 0; i <= 36; i++)
{
if (i >= 1&&i<=monthEnd)
{
DayText[i].GetComponent<Text>().text = i.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Tuesday:
for (int i = 0; i <= 36; i++)
{
days = i - 1;
if (i >= 2 && i <= monthEnd+1)
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Wednesday:
for (int i = 0; i <= 36; i++)
{
days = i - 2;
if (i >= 3 && i <= monthEnd+2 )
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Thursday:
for (int i = 0; i <= 36; i++)
{
days = i - 3;
if (i >= 4 && i <= monthEnd + 3)
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Friday:
for (int i = 0; i <= 36; i++)
{
days = i - 4;
if (i >= 5 && i <= monthEnd + 4)
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;

case DayOfWeek.Saturday:
for (int i = 0; i <= 36; i++)
{
days = i - 5;
if (i >= 6 && i <= monthEnd + 5)
{
DayText[i].GetComponent<Text>().text = days.ToString();
}
else
{
DayText[i].GetComponent<Text>().text = "";
}
}
break;
}
}
}

Textの書き替えを行うので、
名前空間にusing UnityEngine.UIを宣言します。

あとはエディターのスクリプトにオブジェクトをアタッチすれば
問題なく動くかと思います。

上記スクリプトをプレイすると
注意の表示がConsoleに出ます。

CS0414のエラーが出ると思うのですが、
これは day = now.Day で日の取得をしているのですが、
どこにも使ってないのでエラーが出ます。
鬱陶しいなら削除してもらってOKでっす。

残しておいても無問題です。


一応、万年カレンダーが出来ました。
必要の無い枠を灰色に塗りつぶしたりできると良いのですが、
面倒なんで、技術力がないので後日に回します。Ψ( ̄∀ ̄)Ψケケ




追記
日付のテキストを37個用意すると書いたのに、
オブジェクトの連番を0~37にしていました。

0~36の間違いなので修正しました。


2019/03/04追記分
コード表の行間を調整しました。
コード内の<>が消えてしまっていたので、
修正しました。

この記事へのコメント