カウントダウンのアルゴリズム

(1人でカウントダウン)

普通に差分書けばOK

(みんなでカウントダウン)

上記の方法では全員ずれが出る

ローカルの時計とNTPの時計を比較して差分を出し,その時間でカウントダウンする.カウントダウンは直前にしか見ないのでNTPの同期は1回でOK.

ただしコルーチンを頻繁に呼び出すのが怖かったので,1秒でコルーチンしている.つまり最大で0.99秒ずれる.

最近の端末はおもったより性能が良いので,0.1秒でコルーチンしてもいいかもしれません.(NTP呼び出しは1回だけにする)

 

改訂版:CSV読み込み

CSVのテキストはテキスト内に\nとかいてても,改行と認識されない.そこで,強制的に@を改行コードに読み替える方法で回避する

テキストの改行カ所に半角の@を入れる.そこが改行される.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;//use for file read
using UnityEngine.UI;//use unity gui

public class quiz : MonoBehaviour {
	//public string[] qq;
	// Use this for initialization


	public ArrayList qq =new ArrayList();//question store to this array
	private int no;//this means number
	public int maxQ = 3;//count of quiz
	public Text quizText, kaisetsu; //Text用変数
	public Text bt1,bt2,bt3,bt4;//for assign btn tex
	public int answer;//answer number
	public int selected;//selected btn
	//public Toggle tog1, tog2, tog3, tog4;
	public ToggleGroup tgg1;//select btn group

	public GameObject kaisetsuPanelAlert;
	public GameObject seikaiPanelAlert;
	public GameObject ngPanelAlert;
	public GameObject selectAlert;

	void Start () {

		//Android is load from streming Assets floder
	 //
		//jar:file://" + Application.dataPath + "!/assets" + "/";  //example
		//
//		TextAsset csv = Resources.Load("question") as TextAsset;
//		StringReader reader = new StringReader(csv.text);
//		while (reader.Peek () > -1) {
//			string line = reader.ReadLine ();
//			string[] values = line.Split (',');
//			Debug.Log (values[0]);
//		}
		kaisetsuPanelAlert.SetActive (false);
		seikaiPanelAlert.SetActive (false);
		ngPanelAlert.SetActive (false);
		selectAlert.SetActive (false);
		//
		no = 0;//initialize of quiz count

		//ArrayList qq =new ArrayList();
		TextAsset csv = Resources.Load("question") as TextAsset;
		StringReader reader = new StringReader(csv.text);
		while (reader.Peek () > -1) {
			qq.Add (reader.ReadLine ());
		}

		//Debug.Log ((string)qq[1]);
		qread ();//show first question
	}
	
	// Update is called once per frame
	void Update () {
	
	}



	public void tog1press(){
		answer = 1;
	}

	public void tog2press(){
		answer = 2;
	}

	public void tog3press(){
		answer = 3;
	}

	public void tog4press(){
		answer = 4;
	}

	public void qread(){//next mondai

	//	for(no  = 0; no < maxQ; no++){
			
		tgg1.SetAllTogglesOff();
		string qqq=(string)qq[no];
		string[] values = qqq.Split(',');//values is string array


		//GameObject.Find ("SectionText").GetComponent<Text>().text=Section[ int.Parse(values[0]) ];
	//	string atxt = "";
	//	string atxt1 = "";
	//	string atxt2 = "";
	//	string atxt3 = "";
//		int mode = Random.Range (0, 10);
//		if (mode == 0)
//		{
//			atxt=values[2];GoodNo=1;
//		atxt1=values[1];
//			atxt2=values[4];
//			atxt3=values[5];
//		}

		string test1 = (string)values[0];
	//	quizText.text = (string)values[0];
		quizText.text = test1.Replace("@", "\n");

		selected = int.Parse((string)values [1]);//csv is text, so, covert to string
		bt1.text = (string)values[2];
		bt2.text = (string)values[3];
		bt3.text = (string)values[4];
		bt4.text = (string)values[5];

		string kaisetsuKaigyou = (string)values[6];
		kaisetsu.text = kaisetsuKaigyou.Replace("@", "\n");
		//kaisetsu.text = (string)values[6];

		if (no < maxQ) { //quiz count limit
			no++;
		}else{
			no = maxQ; //kokode resulthe,,
		}

//		Debug.Log(tgl);
		//}
	}


	public void answerCheck(){
		//Boolean selectCheck = tgg1.AnyTogglesOn;
		if (!tgg1.AnyTogglesOn()) {
			selectAlert.SetActive (true);
			return;
		}
			if (selected == answer) {
			seikaiPanelAlert.SetActive (true);
	//		Debug.Log ("seikai");
		} else {
			ngPanelAlert.SetActive (true);
		//	Debug.Log ("No!");
		}
	}

	public void hideKaisetsuPanel(){
		kaisetsuPanelAlert.SetActive (false);
		qread ();
	}

	public void showKaisetsunOK(){
		seikaiPanelAlert.SetActive (false);
		kaisetsuPanelAlert.SetActive (true);
	}
	public void showKaisetsuNG(){
		ngPanelAlert.SetActive (false);
		kaisetsuPanelAlert.SetActive (true);
	}

	public void hideSelecyAlert(){
		selectAlert.SetActive (false);
	}

}

 

UGUIのテキストをSQLでチェックするPHP

そのユーザ名が存在するかチェックする

<?php
    // Send variables for the MySQL database class.
    $database = mysql_connect('myserver.jp', 'username', 'passwd') or die('Could not connect: ' . mysql_error());
    mysql_select_db('dbname') or die('Could not select database');
	
	
	
	//変数宣言
$keyword='';	//不正防止用キーワード
$resultData='';	//返すデータ
 
//データ受け取り
if(isset($_POST['keyword'])){
  $keyword=$_POST['keyword'];	//キーワード
  
}
//print $_POST['keyword'];
$name= $_POST['keyword'];
//print $name;
//if($keyword==null or !($keyword==$key['get_score_ranking'])){
//  $redirectUrl = "http://filmm.info/404.html";
//  header("HTTP/1.0 404 Not Found");
//  print(file_get_contents($redirectUrl));
//  exit;
//}



//search test
	$nameresult = mysql_query("SELECT * FROM `scores` WHERE `name` = $name");//文字列検索はシングルクォート
if (!$nameresult) {
    die('クエリーが失敗しました。'.mysql_error());
}
//echo mysql_num_rows($nameresult);
	print mysql_num_rows($nameresult);
	
?>

かなり不要部分多し

ちょっとセキュリティ的には良くない部分有り

スコアを追加するPHP

Unityから呼び出したサーバ側に設置するPHPです

<?php 
        $db = mysql_connect('severurl', 'sq_lusername', 'sql_password') or die('Could not connect: ' . mysql_error()); 
        mysql_select_db('sql?database_name') or die('Could not select database');
 
        // Strings must be escaped to prevent SQL injection attack. 
        $name = mysql_real_escape_string($_GET['name'], $db); 
        $score = mysql_real_escape_string($_GET['score'], $db);
		$cName = mysql_real_escape_string($_GET['cName'], $db); 
        $hash = $_GET['hash']; 
 
        $secretKey="****"; # Change this value to match the value stored in the client javascript below 

        $real_hash = md5($name . $score . $cName . $secretKey); 
        if($real_hash == $hash) { 
            // Send variables for the MySQL database class. 
            $query = "insert into scores values (NULL, '$name', '$score', '$cName');"; 
            $result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
        } 
?>

スコアを追加できます

MySQL(5.5)にテーブルを追加(ハイスコア用)

ハイスコア記録用のCSスクリプト組んでます.

たぶんscoresというテーブルを作って,ID番号(自動インクリメント),名前,スコア,cName(独自に追加)を要素にするというものだと思う

CREATE TABLE `scores` (
   `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
   `name` VARCHAR(128) NOT NULL DEFAULT 'anonymous',
   `score` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`cName` VARCHAR(128) NOT NULL DEFAULT 'anonymous'

)
ENGINE=MyISAM;

最後のENGINE=MyISAMはmySQL5.5用

4.xだと

ENGINE=MyISAM;

だそうです

追加

CREATE TABLE `scores` (
   `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
   `name` VARCHAR(128) NOT NULL DEFAULT 'anonymous',
`cName` VARCHAR(128) NOT NULL DEFAULT 'anonymous',
`area` VARCHAR(128) NOT NULL DEFAULT 'anonymous',
`quize` BOOL NOT NULL DEFAULT '0',
   `score` INT(10) UNSIGNED NOT NULL DEFAULT '0'
`time` INT(10) UNSIGNED NOT NULL DEFAULT '0'
)
ENGINE=MyISAM;

 

トグルボタンをON/OFFでHaloを出す

Haloはライトオブジェクトにあてるとぼうっと光った雰囲気を出せます.(component>effectにはいっています)

トグルボタンを押した感を出すためにHaloをON/OFF表示させるためのスクリプトです

 

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class btnshowHalo : MonoBehaviour {
	public GameObject lightHalo;//assign activate object

	public Toggle toggleBtn;//for assign toggle btn

	// Use this for initialization
	void Start () {
		lightHalo.SetActive(false);//initialize
	}
	
	// Update is called once per frame
	void Update () {
	
	}


	public void showHalo(){

		if (toggleBtn.isOn) {
			lightHalo.SetActive (true); //show halo
		} else {
			lightHalo.SetActive (false); //hide halo
		}
	}
}

アサインすると,トグルボタンとアクティベートするゲームオブジェクトをエディタでアサインします.ボタン押された時のスクリプト設定をやって完了

ntpサーバを使って,デバイス間の時刻修正

カウントダウンアプリをつくっていますが,各自のスマホの時間がバラバラなので,ntpから差分とった時刻で正確な時間を出すようにしてみました.

修正中ですが,概ねこれで作動します.

元ネタはこれ→ http://ftvoid.com/blog/post/847

HTML5ならもう少し簡単だったのですが,,

using UnityEngine;
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using UnityEngine.UI;//use unity gui

// NTP同期時刻を管理するクラス
public class NtpDate : MonoBehaviour {
	private DateTime ntpDate;   // NTP同期時刻
	private float rcvAppDate;   // NTP通信時のアプリ時刻

	private IPEndPoint ipAny;
	private UdpClient sock;
	private Thread thread;
	private volatile bool threadRunning = false;
	private byte[] rcvData;

	public string myString; //for time display
	private DateTime endDate; //for end date
	public Transform myButton; //get button object
	public Text countText; //Text用変数
	private TimeSpan zure; 

	// 初期化
	void Start() {
		// リクエスト実行
		SyncDate();
		// 時刻表示(デバッグ用)
		StartCoroutine(ShowSyncDate());

		//Debug.Log (System.DateTime.Now);
	}

	// 同期時刻の表示
	private IEnumerator ShowSyncDate() {
		while ( true ) {
			yield return new WaitForSeconds(0.1f);//0.5f

			timeCheck ();// do timeCheck() every 1 second

			//if ( Date == false ) {
			if ( Date == null ) {
				Debug.Log("Time is not received.");
			} else {
			//Debug.Log("Receive date : " + Date.ToString());
				//	Debug.Log (DateTime.Now);//  DateTime.Now is confiured time
			}
		}
	}


	/// for GUI my add
	public void timeCheck(){

	endDate = new DateTime(2016, 4, 2, 18,30,00); //本番用

	//	endDate = new DateTime(2016, 2, 4, 12,30,00);//for test
		TimeSpan sabun = endDate - DateTime.Now; //compare now time and end time

//		Debug.Log ("before" + sabun);
//		Debug.Log ("after" + (sabun - zure));


		//int result = TimeSpan.Compare(endDate, DateTime.Now);// cant use
		sabun = sabun - zure;//zure naosu
		if (sabun.Days <= 0 && sabun.Hours <= 0 && sabun.Minutes <= 0 && sabun.Seconds <= 0) { //sabun is all zero and minus
			myString = "Light It Up Blue!"; //message of button
			countText.text = myString; //put text to GUItext
			myButton.GetComponent<Toggle>().interactable = true; //unlock the button
		} else {
			myString = string.Format ("{0:00}D{1:00}H{2:00}M{3:00}S", sabun.Days, sabun.Hours, sabun.Minutes, sabun.Seconds);//string format
			countText.text = myString; //time remaining
			myButton.GetComponent<Toggle>().interactable = false; //lock button before last time
		}



	}

	public void Update(){
	//	timeCheck ();

	}


	// アプリケーション終了時処理
	void OnApplicationQuit() {
		if ( thread != null ) {
			thread.Abort();
		}
		if ( sock != null ) {
			sock.Close();
		}
	}



	// 時刻同期を行う
	public void SyncDate() {
		// リクエスト実行
		threadRunning = true;
		thread = new Thread(new ThreadStart(Request));
		thread.Start();

		// リクエスト待機コルーチン実行
		StartCoroutine(WaitForRequest());

		Debug.Log("Thread is started.");
	}

	// NTPサーバに対してリクエストを実行する
	private void Request() {
		// ソケットを開く
		ipAny = new IPEndPoint(IPAddress.Any, 123);
	//	sock = new UdpClient(ipAny);
		sock = new UdpClient();

		// リクエスト送信
		byte[] sndData = new byte[48];
		sndData[0] = 0xB;
		sock.Send(sndData, sndData.Length, "ntp.jst.mfeed.ad.jp", 123);

		// データ受信
		rcvData = sock.Receive(ref ipAny);
		// 実行中フラグクリア
		threadRunning = false;
	}

	// リクエスト待機コルーチン
	private IEnumerator WaitForRequest() {
		// リクエスト終了まで待機
		while ( threadRunning ) {
			yield return 0;
		}

		// アプリ時刻保存
		rcvAppDate = Time.realtimeSinceStartup;
		Debug.Log (rcvAppDate);
		// 受信したバイナリデータをDateTime型に変換
		ntpDate = new DateTime(1900, 1, 1);
		var high = (double)BitConverter.ToUInt32(new byte[] { rcvData[43], rcvData[42], rcvData[41], rcvData[40] }, 0);
		var low = (double)BitConverter.ToUInt32(new byte[] { rcvData[47], rcvData[46], rcvData[45], rcvData[44] }, 0);
		ntpDate = ntpDate.AddSeconds(high + low / UInt32.MaxValue);

		// UTC→ローカル日時に変換
		ntpDate = ntpDate.ToLocalTime();
		zure = Date - DateTime.Now;
			Debug.Log ("zure" + zure);
	}

	// NTP同期時刻
	public DateTime Date {
		get {
			
			return ntpDate.AddSeconds(Time.realtimeSinceStartup - rcvAppDate);
		}
	}
}