2017年4月17日 星期一

製作日誌--019

上周剛好遇到期中考周
於是偽春菜就又被放置一周了

上次把鬧鐘功能補齊
這次就只是稍微改得更方便一點

###鬧鐘###

**鬧鐘開關**
上次完成的鬧鐘在設定第一個時間的時候
並不會自動把鬧鐘打開
個人實測的時候還蠻常忘記打開的
所以在加入第一個鬧鐘的時候
會強制把該功能打開

**與備忘錄連動**
在備忘錄的選單下面新增了"鬧鐘連動"的選項
打開這項功能
偽春菜便會在鬧鐘響完之後把備忘錄顯示出來

之後如果有機會新增更多功能的話
也會考慮像這樣的連動機制

==============================================================
今天幫小鳥弄了新表情
不過還沒上色




















這次是疑惑或思考的表情...雖然不太像@@
之後應該會在頭旁邊加個問號
強行疑惑@@

2017/04/17

2017年3月27日 星期一

製作日誌--018

上周在趕作業,就沒有更新了
於是今天的更新決定來個大的
本日將鬧鐘系統搭載完成
以及將失望表情的動畫完成

###鬧鐘###

實作上跟備忘錄差不多,因為兩者本質上是類似的
一樣有新增項目、查看、刪除、清空與開關的功能
不過鬧鐘這裡不一樣的是
只要是沒有設定任何鬧鐘的情況下
開關就一定是關著的狀態
且不被允許在沒有鬧鐘的情況下打開鬧鐘
這個部分下面會詳細說明

由於各項鬧鐘功能的實作和備忘錄十分相似
這裡只說明基本鬧鐘的做法
首先先在初始參數中宣告"alarmtime"、"alarmnum"、"alarmopen"三個變數
alarmtime是用來儲存全部的鬧鐘時間
其形態是以字串為元素的陣列
alarmnum代表目前設定的鬧鐘個數
alarmopen則是用來判斷目前鬧鐘是否為開啟狀態
接著看到這段程式碼:

SetAlarm
{
    "\0\s[0]想要小鳥在什麼時候叫%(username)呢?\![open,timeinput,OnSetAlarm]\e"
}

OnSetAlarm
{
    tmpalarmtime = reference0
    time = SPLIT(reference0,",")
    "\0\s[0]在%(time[0])時%(time[1])分的時候提醒%(username)嗎?\n\n/
    \q[○是,SetAlarmYes]\n/
    \q[○否,SetAlarmNo]\e"
}

Select.SetAlarmYes
{
    alarmtime ,= tmpalarmtime
    alarmnum += 1
    "\0\s[0]我知道了!\e"
}

Select.SetAlarmNo
{
    "\0\s[0]真的不需要小鳥提醒%(username)嗎?\w8\1\s[10]總是不能什麼事都依賴妳吧。\w8\0\s[0]\n\n嗯…\w5我知道了。\e"
}

教學文章進行至此,相信讀者應該都已經對SakuraScript有一定的了解了
這裡就簡單說明這段程式碼的功能
首先是先詢問使用者想要設定鬧鐘的時間
輸入的時間會被當作字串傳入OnSetAlarm
我們只取時與分
然後詢問使用者輸入是否正確
是的話就新增鬧鐘,不是就不做任何修改

再來我們直接跳過其他功能的實作
這些功能的寫法都可以參考備忘錄的作法
詳情請閱讀製作日誌--014製作日誌--017
以下我們看到鬧鐘實踐的方式:

if alarmopen == 1
{
    for i = 0; i < alarmnum; i ++
    {
        time = alarmtime[i]
        time = SPLIT(time,",")
if hour == time[0] && minute == time[1]
        {
"\0\s[0]現在是%(time[0])時%(time[1])分喔!\![sound,play,alarm.mp3]\x\![sound,stop]"
--
"\0\s[0]唉嘿嘿,小鳥有記得提醒%(username)喔!"
"\0\s[2]咦…\w5…\w5…\w8\n%(username)怎麼一臉呆滯?\w8\1\s[10]該不會是小鳥妳把%(username)給嚇傻了吧。\w8\0\s[0]\n\n真的嗎!?\w5\n對不起!%(username)!"
--
"\e"
break
}
    }
}

這段程式碼放在OnMinuteChange事件中
每一分鐘過去就檢查一次是否有鬧鐘該響鈴
首先檢查鬧鐘是否開啟
接著用for迴圈將所有鬧鐘檢查過一輪
一旦找到一個符合的時間就響鈴
而響鈴的辦法是使用\![sound,play,alarm.mp3]
這個指令會去自動搜尋放在人格資料夾下稱為alarm.mp3的檔案
並且播放
接著看到\x指令
這個指令會使目前對話框停滯,直到使用者去點擊對話框為止
點擊後對話框會直接刷新,進行之後的對話
而我們可以看到,在\x指令後接著\![sound,stop]的指令
因為使用者點擊就代表關掉鬧鐘的動作
這樣就完成鬧鐘功能了

===================================================================
把失望表情的動畫給實作了
之後畫的表情可能會開始脫離偽春菜的預設表情列表
以需要用到的為優先考量


via GIPHY

有點鬧彆扭的樣子真可愛

2017/03/27

2017年3月13日 星期一

製作日誌--017

難得的準時更新
今天把備忘錄功能補齊了
因為偽春菜本身有自己的一套語法
很多操作上有不少限制
只能用比較取巧的方式去處理一些功能

###備忘錄###

本日的更新內容只有備忘錄
因為刪除項目的部份讓我研究了一兩個小時
以下開始說明實作方法

首先,假設我們的備忘錄參數memo現在長這樣子:

memo = "0\n1\n2\n"

在查看備忘錄時,因為有\n的存在,看起來就像條列式的清單
為了讓使用者可以選取想要刪除的項目
我們先用AYA的內建函數SPLIT來處理字串:

memoarr = SPLIT(memo,"\n")

上面的操作會將memo分割成陣列,而分割點為\n
在這裡便會將memo分割成["0","1","2",""]
需注意最後面會多出一個空字串
因為SPLIT的分割模式是一但看到分割點
就會將前後分別做為單一字串放入陣列
因此在memo中最後一個\n的前後分別是"2"與""
這個部分讓我頭痛了一陣子
所以在這裡特別提醒一下
接著就是要將選項輸出了

大家應該還記得選項的輸出方式為:

\q[標題,ID]

但是這裡就有個問題,我們存在memoarr中的元素皆為字串
而上面程式碼中的標題本身就只是一串字,而不是字串
只是傳入內碼的時候會作為字串傳入而已
因此要怎麼將memoarr中的字串成功在選項中被顯示呢?
這時就可以利用AYA內建函數STRFORM
該函數的功能與C語言中的sprintf相似
以下便說明如何將memoarr的每個字串化為一個一個選項輸出:

question = STRFORM("\q[○$s,OnRemoveMemo,$d]\n", i, cnt)

可以看到STRFORM的傳入值為一個有點怪怪的字串
首先,上面程式碼中的$s和$d代表這個地方要由後面提供的變數來替換
s代表string,代表替換變數型態為字串
而d代表decimal,其他應該都與C語言相同
因此上面程式碼中的$s與$d會分別被變數i與cnt代換
而question變數便會存放"\q[○(字串i),OnRemoveMemo,(整數cnt)]\n"
這樣便是一個選項的正確形式了
由於選項中的ID為On開頭的函數,後面的參數會作為reference0、reference1...等等傳入ID函數
而這裡的傳入值是cnt變數所存放的整數
而前面也提過了,傳入值會以字串傳入

現在要將memoarr的內容全部變成選項:

cnt = 0
talk = "\0\s[0]"
foreach memoarr;i
{
    if i != ""
    {
        question = STRFORM("\q[○$s,OnRemoveMemo,$d]\n", i, cnt)
        talk = talk + question
        cnt = cnt + 1
    }
}

首先看到前面兩行
cnt為計數器,記錄memoarr中每個字串的序數
而talk為最後要讓偽春菜說出的話,因此初始值為開啟對話框與設定表情
接著看到foreach迴圈,其功能跟python中的for i in list相同
這裡便是以變數i作為迭代器,將memoarr中每個元素迭代一輪
每一次迭代都檢查i是否為空字串,避免生成不該存在的選項
這個動作便是在防範前面提到SPLIT分割模式所造成的問題
再來看到STRFORM,裡面我將每個選項的序數做為reference0傳入OnRemoveMemo
用途下面會說明
每跑完一圈,cnt就加1,以得到正確的序數
最後看到OnRemoveMemo怎麼處理刪除動作

OnRemoveMemo
{
    memoarr = SPLIT(memo)
    idx = TOINT(reference0)
    cnt = 0
    memo = ""
   
    foreach memoarr;i
    {
        if (idx != cnt) && (i != "")
        {
            memo = memo + i + "\n"
        }
        cnt = cnt + 1
    }
}

這裡的操作基本與前一段程式碼相同
首先看到idx變數,由於reference0會被當作字串傳入
因此要用AYA內建函數TOINT將字串轉為整數
接著在memo被更動前再一次將其分割並存入memoarr變數中
然後將memo清空並重新將沒被刪除的內容填入
這裡重新填入同樣用到foreach迴圈
但是判斷式稍微不一樣
要兩個條件同時為真才會執行if內的動作
第一個是idx不等於cnt,這個判斷是確認當前i的序數是否等於待刪除項目的序數
第二個是i不為空字串,這個判斷的理由在前面已經提過了
在字串內部做刪除這個動作比較困難
但刪除這個動作等價於將剩下的記錄下來
因此我們便是用後者處理刪除的動作

這裡的解說只提了最核心的功能
而我另外還追加了選擇刪除項目時,最下面會出現取消選擇肢
可以用來防按錯的情況
而當memo被刪除所有項目後
會自動還原到空白的狀態
以及當memo為空白狀態時
小鳥會提醒使用者目前備忘錄是空白而不進行刪除工作

==================================================================
今天是把失望表情完成之後才開始編程工作
但並沒有將表情實裝,只有將設定檔寫好而已


一個計畫拖越久,畫風就變越多
這是一件無比哀傷的事情

2017/03/13

2017年3月6日 星期一

製作日誌--016

228連假沒了電腦,又休息一周
不過這周的進度也很少

###AI對話###

新增了兩個對話
突然想到所有很電波的事情都丟給啾啾來說就好了
這樣應該可以增加不少對話內容

###備忘錄###

稍微調整了一下備忘錄所用到的函式

=============================================
由於先寫了些作業,能畫圖的時間剩不多
就先把失望表情給描起來而已


下周應該就可以上色了

2016/03/06

2017年2月20日 星期一

製作日誌--015

開學了,之後的更新進度應該會和以前一樣一周一次
不知不覺這個網誌也更新到第15篇文章了
但每周的進度倒是越來越少

今天程式的更新不多
但是畫了新表情的草稿

###溝通###

在與小鳥對話時如果輸入中包含"出門"或"出發"的話
小鳥會問使用者是否要出門了
如果選是,偽春菜會在對話後自動關閉
這裡我銜接到了備忘錄的功能

###備忘錄###

新增初始參數goout,作為判斷是否在使用者出門時印出備忘錄
出門提醒的開關在主菜單的備忘錄選單內
設計上就只是在memo.dic中加上ChangeGoOut的函式
並且在備忘錄選單中新增選項與對話
設計非常簡單,便不詳細說明程式碼了

======================================================

這次的新表情是失望,但畫起來感覺像是鬧彆扭


將近兩個月沒有畫小鳥了,希望沒有太奇怪

2017/02/20

2017年1月30日 星期一

製作日誌--014

小鳥偽春菜已經一個月沒有更新了
一開始放假就失去了時間感
直到今天早上才想起今天是星期日
幸好休息一個月之後沒有失去戰鬥力

本次更新有一項非常重要的事情必須宣布
「南小鳥人格新增了備忘錄功能 」
雖然是臨時想到可以新增的功能
作為對SakuraScript的練習也是個不錯的挑戰
目前只有最基本的功能
詳細會在下面做討論

###溝通###

新增了一些溝通內容
備忘錄功能就是在增加溝通對話時想到的主意

###備忘錄###

本次更新的重點
以下會說明一下個人的對此功能的構想與做法
首先,我認為備忘錄不應該因為偽春菜主程式的關閉而消失
且應該可以隨時新增項目、查看、清空和刪除項目
並且在使用者要出門前可以提醒要帶出去的東西

接著是我已完成的部分
現在只有新增項目、察看與清空的功能
出門提醒的功能還沒引入,但這部份很簡單

首先我先在aya_bootend.dic的OnFirstBoot事件中加入兩個參數

memo = "<空白>"
memoisblank = 1

memo參數的型態為字串(string),這個參數就是整本備忘錄
而memoisblank則是用來判定memo目前是否為空白
由於個人不希望當備忘錄空白時,查看備忘錄時就真的看到空白
因此用"<空白>"來表示目前沒有記錄任何項目

接下來則是加入一些專門處理memo的函式
先新增一個叫做aya_memo.dic的檔案
(詳細處理方式請參考005─**事件處理**的部分)
並在裡面加入兩個函式

ClearMemo
{
    memo = "<空白>"
    memoisblank = 1
}

OnAddMemo
{
    if reference0 != ""
    {
        if memoisblank == 1
        {
            memo = ""
            memoisblank = 0
        }
        memo += reference0 + "\n"
    }
    "\0\s[0]已經幫%(username)把提醒寫進備忘錄囉!\e"
}

第一個ClearMemo就是將備忘錄整個清空
第二個OnAddMemo則為新增項目
先判定使用者有無輸入內容
如果有,再判斷目前備忘錄是否為空白
如果有輸入且備忘錄為空白
則將memoisblank設為0,且將memo設為""
再加入新增項目
如果有輸入且備忘錄已有內容
則直接加入新增項目
如果沒有輸入內容
小鳥依然會告訴你已更新備忘錄
但其實她什麼也沒做(.8.)

再來就是要提供使用者存取備忘錄的方式
首先在OpenMenu中多加上這行

"\q[○備忘錄,memo]\n"

這樣在開啟主菜單時便會多出備忘錄的選項
接著再加上

Select.memo
{
    Memo
}


Memo
{
    "\0\s[0]\n\n/
    \q[○查看,open_memo]\n/
    \q[○新增項目,add_memo]\n/
    \q[○清空,clear_memo]\e"
}


如此一來使用者便可以在主菜單找到備忘錄的功能了
接著便要寫入現有的三個互動式功能
第一個是"查看"

Select.open_memo
{
    "\0\s[0]這是%(username)的備忘錄:\n\n%(memo)\e"
}

這裡應該不需要多加說明了,就只是將memo作為變數存取並顯示出來而已

第二個是"新增項目"

Select.add_memo
{
    "\0\s[0]請問要加入什麼項目呢?\![open,inputbox,OnAddMemo,-1]\e"
}

這裡我們注意到新的語法\![open,inputbox,OnAddMemo,-1]
這個語法的功能是打開輸入視窗,等待使用者輸入內容
詳細說明如下:
\![open,inputbox,ID,(等待時間),(預設輸入內容)]
(括號內的內容為選擇性參數,可輸入可不輸入)
首先,由於開啟了inputbox
如果使用者有輸入內容,則會引發OnUserInput事件
而ID會被當作reference0被傳入OnUserInput作為事件名稱
而使用者輸入內容則是作為reference1被傳入
等待時間以毫秒為單位,超過等待時間就會自動關閉輸入框
若等待時間為-1,則代表輸入框會等到使用者輸入內容或自行關閉才會關閉
而預設輸入內容則是在輸入框出現時
顯示在輸入欄的文字
作為提醒使用者目前該輸入的內容,是個貼心的設計

這個語法有個特別的用法
當ID所表的事件名稱為On開頭,會跳過OnUserInput事件
直接執行ID所表示的事件名稱
也就是上面範例碼的用法
因此當使用者輸入(或沒有輸入)內容時
程式會自動跳轉到aya_memo.dic內定義的OnAddMemo函式
這也是會什麼該函式要以On作為開頭的原因

第三個是"清空"

Select.clear_memo
{
    "\0\s[0]這就幫你把備忘錄清空!\e"
    ClearMemo
}

以上是目前備忘錄的功能
對特定內容作刪除可能還需要更多字串處理的工作
然後不排除會新增備忘錄的其他功能

###唱歌###

將唱歌的表情實裝
但發現唱歌時不會執行動嘴的動畫

=============================================================

雖然畫了唱歌時的動嘴動畫
但上面已經說了
並不會被執行
因此今天沒有新的圖檔

2017/01/30