JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
/*背包變數*/ <<set $inventory to []>> /*插入音源*/ <<cacheaudio "mp3" "media/mp3.mp3">> <<cacheaudio "wav" "media/wav.wav">> <<cacheaudio "wavondrive" "https://el1l7eqjck7zfbkn6duica-on.drv.tw/%E5%B7%A5%E4%BD%9C/twine/audio/wav.wav">> <<cacheaudio "mp3ondrive" "https://el1l7eqjck7zfbkn6duica-on.drv.tw/%E5%B7%A5%E4%BD%9C/twine/audio/mp3.mp3">> /*googledrive的網址要透過https://drv.tw/去獲取音檔的連結*/ /*變數範例*/ <<set $hp to 10>> <<set $playerequip to ['裝備1','裝備2','裝備3','裝備4']>> /*進度條*/ <<set $level to 20>> /*下拉式選單*/ <<set $listget to []>> <<set $item to []>> <<set $listoption to ['物品1','物品2','物品3']>>
[[背包]]
Twine互動小說軟體語法範例
-Sugarcube格式- [[回首頁|start]]
製作者:<a href="https://www.facebook.com/3zhiyu" target="_blank">三知餘文化</a>
---- Twine學習資源 [[Twine官網|https://twinery.org/]] [[Twine wiki|https://twinery.org/wiki/]] [[sugarcube技術文件|http://www.motoslave.net/sugarcube/2/docs/]] [[cookbook|https://twinery.org/cookbook/]] [[遊戲發問:reddit|https://www.reddit.com/r/twinegames/]] [[遊戲發問(已停止更新):TwineQ&A|http://twinery.org/questions/]] [[Twine常見QA問答集|https://hackmd.io/@lanisairhead/BkhDqVSk6]] ----
<h3>基礎語法</h3> * [[文字樣式]] * [[連結互動]] * [[圖片]] * [[影片]] * [[配樂音效]] ---- <h3>進階語法</h3> * [[變數]] * [[取代內容]] * [[輸入指定Passage內容]] * [[進度條]] * [[輸入框與按鈕]] * [[下拉式選單]] * [[點擊展開收合]] * [[通知提醒]] * [[返回前頁]] ---- <h3>功能系統</h3> * [[動態效果]] * [[側邊欄選單相關功能]] * [[彈出與關閉小視窗]] * [[數字密碼輸入介面]] * [[圖片預載]] * [[自動存檔]] * [[背包系統|背包功能]] * [[對話系統]] * [[影像地圖系統]] ---- <h3>遊戲功能與Javascript</h3> * [[Javascript使用說明]] ---- <h3>遊戲版面樣式與CSS</h3> * [[CSS使用說明]] * CSS與版面 ** [[CSS基本概念]] ** [[CSS選擇器]] * [[中文網路字型]] * [[動態套件:Animate.css|如何使用Animate.css]] ---- <h3>作品參考</h3> * [[三隻魚歷史新聞台|https://3zhiyu.github.io/%E4%B8%89%E9%9A%BB%E9%AD%9A%E6%AD%B7%E5%8F%B2%E6%96%B0%E8%81%9E%EF%BC%9A%E6%B7%BA%E9%87%8E%E7%BD%B7%E5%B7%A5%E4%BA%8B%E4%BB%B6%E7%B0%BF]] ** 三知餘文化自己開發的實驗性作品,未來的記者穿越回到過去,透過採訪過去的人來探詢歷史事件真相 * [[棒球大聯盟|https://3zhiyu.github.io/2023trainingplan/%E6%A3%92%E7%90%83%E5%A4%A7%E8%81%AF%E7%9B%9F/]] ** 三知餘文化主持的培訓課程成果,以現代人穿越回到戰後初期的馬祖,藉由實際加入棒球隊的經歷,了解馬祖過去棒球文化的發展 * [[《童女之舞》互動劇|http://faculty.stust.edu.tw/~chihchiehyang/dance.html]] ** 以文字、聲音為主要體驗,改編自文學著作《童女之舞》 * [[Wayfarer(全英文)|https://idrellegames.itch.io/wayfarer]] ** 能自訂角色、較多視覺設計元素的文字冒險遊戲 * [[The Temple of No(全英文)|https://crowscrowscrows.itch.io/the-temple-of-no]] ** 經典文字冒險遊戲,簡約的版面、黑白為主的圖片,搭配文字建構出繪本般的故事體驗。 * [[When Twilight Strikes(全英文)|https://evertides.itch.io/when-twilight-strikes]] ** 戀愛模擬的文字冒險遊戲(文字為主、多分支選擇)
在遊戲內新增背包功能,透過變數{{{$inventory}}}儲存獲得的物品,並於"背包"頁面中展示所有獲得的物品。範例請[[見此|背包系統範例]],功能語法請依照以下步驟來建立。 1.建立"背包"Passage,並置入以下語法: @@color:green; {{{ <<if $inventory.length is 0>>\ 沒有東西。 <<else>>\ 你的背包裡有:<<= $inventory.join(', ')>>。 <</if>> }}} @@ 2.新增StoryInit的PASSAGE並加入背包變數與需要的巨集: @@color:green; {{{ <<set $inventory to ['預設擁有物品1','預設擁有物品2']>> }}} @@{{{[ ]}}}中可留空→@@color:green;{{{<<set $inventory to []>>}}}@@ 3.在獲得、失去物品的Passage中,新增以下語法: @@color:green; {{{ <<run $inventory.push('獲得物品名稱')>> <<run $inventory.delete('失去物品名稱')>> }}} @@ 4.進行是否擁有某物品的判定語法(@@color:green;{{{<<if>>}}}@@): @@color:green; {{{ <<if $inventory.includes('某物品')>> 你有某物品 <<else>> 你沒有某物品 <</if>> }}} @@ 5.在側邊欄選單增加背包連結,玩家可以隨時瀏覽擁有的物品: 於StoryMenu的Passage中新增"背包"連結,例如-- @@color:green;{{{[[}}}@@背包@@color:green;{{{]]}}}@@ @@color:green;{{{[[}}}@@物品@@color:green;{{{|}}}@@背包PASSAGE@@color:green;{{{]]}}}@@ <<return 返回>>
<h3>點擊連結後到新的Passage頁面</h3> [[新的Passage]] @@color:green;{{{[[}}}@@Passage名稱@@color:green;{{{]]}}}@@ [[點擊後到新的Passage|新的Passage]] @@color:green;{{{[[}}}@@連結文字@@color:green;{{{|}}}@@Passage名稱@@color:green;{{{]]}}}@@ <<link 點擊後到新Passage的link連結>><<goto 新的Passage>><</link>> @@color:green;{{{<<link}}}@@ 連結文字 Passage名稱@@color:green;{{{>><</link>>}}}@@ <table class="border-table"> <p>@@color:black;"""<<link>>巨集進階說明"""@@</p> >@@color:green;{{{<<link}}}@@ 連結文字@@color:green;{{{>>}}}@@ >>(點擊連結後要進行的動作) >@@color:green;{{{<</link>>}}}@@ <p><strong>@@color:red;(點擊連結後要進行的動作)@@</strong>的部分可以使用其他巨集或執行Javascript,讓點擊文字之後發生該巨集或Javascript的功能。</p> <p>比如使用: @@color:green;{{{<<goto>>}}}@@巨集→點擊文字後到其他Passage或網址。 @@color:green;{{{<<set>>}}}@@巨集→點擊文字後更動或獲得變數數值。 @@color:green;{{{<<script>>alert("This is JavaScript.");<</script>>}}}@@→點擊連結後執行彈出警告視窗的javascript程式碼。</p> </table> ---- <h3>點擊連結後,連結內容變化成新內容</h3> 點擊之後,連結文字後生成新內容:<<linkappend '點擊文字'>>新的內容<</linkappend>>。 @@color:green;{{{<<linkappend}}}@@ '點擊文字'@@color:green;{{{>>}}}@@新的內容@@color:green;{{{<</linkappend>>}}}@@ 點擊之後,連結文字前生成新內容:<<linkprepend '點擊文字'>>新的內容<</linkprepend>>。 @@color:green;{{{<<linkprepend}}}@@ '點擊文字'@@color:green;{{{>>}}}@@新的內容@@color:green;{{{<</linkprepend>>}}}@@ 點擊之後,連結文字被新內容取代:<<linkreplace '點擊文字'>>新的內容<</linkreplace>>。 @@color:green;{{{<<linkreplace}}}@@ '點擊文字'@@color:green;{{{>>}}}@@新的內容@@color:green;{{{<</linkreplace>>}}}@@ 點擊之後,開啟[[外連網址|https://www.google.com/]] @@color:green;{{{[[}}}@@外連網址@@color:green;{{{|}}}@@{{{https://www.google.com/}}}@@color:green;{{{]]}}}@@ !!!!進階功能:文字淡出 在"""<<linkappend>>、<<linkprepend>>、<<linkreplace>>"""的語法中加入@@color:red;t8n@@,新的文字內容會以淡出的方式出現。 <<linkprepend '文字淡出' t8n>>新的內容<</linkprepend>> @@color:green;{{{<<linkprepend}}}@@ '文字淡出' @@color:green;{{{t8n>>}}}@@新的內容@@color:green;{{{<</linkprepend>>}}}@@ ---- <h3>點擊連結後,連結內容產生循環變化</h3> 點擊之後,變成其他內容:<<include "循環選項">>。 連結會在固定幾種內容中循環變化 1.新增一個名叫「循環出現」的Passage,將要循環出現的內容打在其中: @@color:green; {{{ <<silently>> <<if not $choices>> <<set $choicesCount to -1>> <<set $choices to ["第一個選項", "第二個選項", "第三個選項"]>> <</if>> <<set $choicesCount to $choicesCount + 1>> <<if $choicesCount >= $choices.length>> <<set $choicesCount to 0>> <</if>> <<set $cyclingResult to $choices[$choicesCount]>> <</silently>>\ <<linkreplace $choices[$choicesCount]>><<include "循環選項">><</linkreplace>> }}} @@ 2.用"""<<include>>"""巨集呈現循環變化的連結內容: @@color:green;{{{<<include "}}}@@循環選項@@color:green;{{{">>}}}@@ <<return 返回>>
新頁面 <<return 返回>>
* [[文字外觀]] * [[文字標題]] * [[項目清單]] * [[文字顏色]] * [[直書排版]] <<return 返回>>
你看到了一把劍,<<link 拾起劍>><<run $inventory.push('大劍')>><</link>>。 劍太重了,<<link 丟棄劍>><<run $inventory.delete('大劍')>><</link>>。 [[觀看背包|背包]] <<return 返回>>
<<if $inventory.length is 0>>\ 沒有東西。 <<else>>\ 你的背包裡有:<<= $inventory.join(', ')>>。 <</if>> <<return 返回>>
<h3>文字標題</h3> 一共有6個階層的標題(1最大~6最小) <h1>標題1</h1> @@color:green;{{{<h1>}}}@@標題1@@color:green;{{{</h1>}}}@@ @@color:green;{{{!}}}@@標題1 ---- <h2>標題2</h2> @@color:green;{{{<h2>}}}@@標題2@@color:green;{{{</h2>}}}@@ @@color:green;{{{!!}}}@@標題2 ---- <h3>標題3</h3> @@color:green;{{{<h3>}}}@@標題3@@color:green;{{{</h3>}}}@@ @@color:green;{{{!!!}}}@@標題3 ---- <h4>標題4</h4> @@color:green;{{{<h4>}}}@@標題4@@color:green;{{{</h4>}}}@@ @@color:green;{{{!!!!}}}@@標題4 ---- <h5>標題5</h5> @@color:green;{{{<h5>}}}@@標題5@@color:green;{{{</h5>}}}@@ @@color:green;{{{!!!!!}}}@@標題5 ---- <h6>標題6</h6> @@color:green;{{{<h6>}}}@@標題6@@color:green;{{{</h6>}}}@@ @@color:green;{{{!!!!!!}}}@@標題6 ---- * [[進階標題]] <<return 返回>>
<h3>文字外觀</h3> //斜體// @@color:green;{{{//}}}@@斜體@@color:green;{{{//}}}@@ @@color:green;{{{<em>}}}@@斜體@@color:green;{{{</em>}}}@@ ---- ''粗體'' @@color:green;{{{''}}}@@粗體@@color:green;{{{''}}}@@ @@color:green;{{{<strong>}}}@@粗體@@color:green;{{{</strong>}}}@@ ---- __底線__ @@color:green;{{{__}}}@@底線@@color:green;{{{__}}}@@ @@color:green;{{{<u>}}}@@底線@@color:green;{{{</u>}}}@@ ---- ==刪除線== @@color:green;{{{==}}}@@刪除線@@color:green;{{{==}}}@@ @@color:green;{{{<s>}}}@@刪除線@@color:green;{{{</s>}}}@@ ---- 字^^上標^^ 字@@color:green;{{{^^}}}@@上標@@color:green;{{{^^}}}@@ 字@@color:green;{{{<sup>}}}@@上標@@color:green;{{{</sup>}}}@@ ---- 字~~下標~~ 字@@color:green;{{{~~}}}@@下標@@color:green;{{{~~}}}@@ 字@@color:green;{{{<sub>}}}@@下標@@color:green;{{{</sub>}}}@@ ---- 引用 > Quote @@color:green;{{{>}}}@@ Quote >> Nested quote @@color:green;{{{>>}}}@@ Nested quote ---- 格式化 @@color:green;{{{"""}}}@@"""//沒有樣式//"""@@color:green;{{{"""}}}@@ ---- <<return 返回>>
!!!項目清單 * 項目 * 另一個項目 @@color:green;{{{*}}} 項目@@ @@color:green;{{{*}}} 另一個項目@@ ---- # 編號 # 下一個編號 @@color:green;{{{#}}} 編號@@ @@color:green;{{{#}}} 下一個編號@@ ---- <<return 返回>>
!!!文字顏色 @@高亮文字@@ """@@高亮文字@@""" 要改其他顏色的話,把前面的「"""@@"""」改成「"""@@color:"""CSS色碼;」。 例如改成@@color:green;綠色@@的話:"""@@color:green;綠色@@""" [[色碼參考|https://www.w3schools.com/cssref/css_colors.asp]] <span style="background-color:red;">文字加底色</span> @@color:green;{{{<span style='background-color:}}}@@@@color:red;red@@@@color:green;{{{;'>}}}@@文字加底色@@color:green;{{{</span>}}}@@ <<return 返回>>
!!!插入圖片 <img src="https://lh3.google.com/u/0/d/1VH6w00nGmchvZDDvn5-uxFoBsrHSRl1H" width="200"> !!!!twine語法: @@color:green;{{{[img[}}}@@media/image.png@@color:green;{{{]]}}}@@ @@color:green;{{{<img src="}}}@@圖片網址@@color:green;{{{"}}}@@ @@color:green;{{{width="}}}@@寬(px)@@color:green;{{{" height="}}}@@高(px)@@color:green;{{{" >}}}@@ * 語法說明 **<<message '1.圖片網址'>> /* "//Google雲端的圖片網址//" """https://lh3.google.com/u/0/d/"""@@color:red;1VH6w00nGmchvZDDvn5-uxFoBsrHSRl1H@@ 紅字部分替換為共享連結的@@color:red;ID@@: """https://drive.google.com/file/d/"""@@color:red;1i47AvIwnj1FtLDOIcTOfiFvYjkzLpK90@@"""/view?usp=sharing""" */ <strong>//Imgur的圖片網址//</strong> """https://i.imgur.com/"""@@color:red;{{{w5JcUUx}}}@@""".jpg""" 在共享連結後加上圖檔格式,比如圖片是jpg格式,就在連結後加上@@color:red;.jpg@@ """https://imgur.com/w5JcUUx"""@@color:red;.jpg@@ <</message>> **<<message '2.圖片的寬高'>> 尺寸預設為px,因為圖片有固定比例,語法中寬、高擇一填入即可。 寬高亦可用%,會依照頁面比例縮放。 <</message>> **<<message '3.以圖片為連結'>> 用圖片作為連結文字,比如點擊圖片後到新的Passage。 <<link [img[media/image.png][新的Passage]]>><</link>> twine語法: @@color:green;"""<<link [img["""@@media/image.png@@color:green;"""]["""@@新的Passage@@color:green;"""]]>><</link>>"""@@ <</message>> * 進階應用:隨機圖片([[重新載入此頁面觀看隨機效果|圖片]]) 隨機出現「線索1.png」~「線索6.png」的圖 <<set _random to random(1,6)>> <<= "<img src='media/img/線索"+ _random +".png' width='100px'>">> twine語法: @@color:green;"""<<set _random to random(1,6)>>"""@@ @@color:green;"""<<="""@@ @@color:green;""""<img src='media/線索" + _random + ".png' width='100px'>">>"""@@ <<return 返回>>
!!!播放音源 !!!!1.新增StoryInit的Passage,加入以下語法: @@color:green;{{{<<cacheaudio}}}@@ '音源名稱' '檔案位置'@@color:green;{{{>>}}}@@ * 語法說明 **「音源名稱」是該音源檔案在故事中的特殊代稱,可自行取名。 **「檔案位置」則是音源檔案所在的地方,可能是資料夾位址或網址,例如:@@color:green;{{{media/mp3.mp3}}}@@ 比如有一個音源檔案,位置放在media資料夾中,檔案名稱為「audio.mp3」,在故事中稱呼為AU。 那麼程式碼這麼寫-- @@color:green;{{{<<cacheaudio 'AU' 'media/audio.mp3'>>}}}@@ 如果音源檔案在google雲端資料夾上,要透過[[drive to web|https://drv.tw/]]去獲取音檔的連結。 @@color:green;{{{ <<cacheaudio 'mp3ondrive' 'https://el1l7eqjck7zfbkn6duica-on.drv.tw/%E5%B7%A5%E4%BD%9C/twine/audio/mp3.mp3'>> }}}@@ !!!!2.在播放音源的Passage上,加入以下語法: @@color:green; {{{ <<link 播放音源>> <<audio '音源名稱' play>> <</link>> }}} @@ 語法呈現結果 <<link 播放音源>><<audio 'mp3' play>><</link>> 停止音源的語法: @@color:green; {{{ <<link 停止音源>> <<audio '音源名稱' stop>> <</link>> }}} @@ 語法呈現結果 <<link 停止音源>><<audio 'mp3' stop>><</link>> ---- !!!!進階方法:內嵌音源 利用iframe內嵌,但會有播放器介面 * 語法 @@color:green; {{{ <iframe frameborder="0" width="500" height="100" src="音源網址(可用google雲端檔案,從檔案的「嵌入項目」中複製src="~"中的網址)"> </iframe> }}} @@ * 呈現結果 <iframe frameborder="0" width="500" height="100" src="https://drive.google.com/file/d/1-VHJLyCV7l3v1Ull3Q2lwMI86KQqFGTi/preview"> </iframe> <<return 返回>>
[[動態效果語法整合]] [[動態效果CSS和JS整合]] !!!!<<message '1.文字置中'>> * 新增CSS程式碼 @@color:grey; {{{ .center { text-align: center; } }}} @@ * 要置中的文字加上語法 @@color:green;{{{@@.center;}}}@@置中文字內容@@color:green;{{{@@}}}@@ @@.center;文字置中範例@@ <</message>> !!!!<<message '2.文字逐行出現'>> <<include '逐行出現'>> <</message>> !!!!<<message '3.固定時間後出現文字'>> <<include '固定時間出現'>> <</message>> !!!!<<message '4.喝醉效果'>> <<include '喝醉效果'>> <</message>> !!!!<<message '5.發抖效果'>> <<include '發抖效果'>> <</message>> !!!!<<message '6.文字加陰影'>> <<include '文字陰影'>> <</message>> !!!!<<message '7.浮凸字體'>> <<include '浮凸字體'>> <</message>> !!!!<<message '8.模糊字體'>> <<include '模糊字體'>> <</message>> !!!!<<message '9.焦糊字體'>> <<include '焦糊字體'>> <</message>> !!!!<<message '10.光暈字體'>> <<include '光暈字體'>> <</message>> !!!!<<message '11.字體鏡射'>> <<include '字體鏡射'>> <</message>> !!!!<<message '12.字體上下顛倒'>> <<include '字體上下顛倒'>> <</message>> !!!!<<message '13.字體閃爍'>> <<include '字體閃爍'>> <</message>> !!!!<<message '14.字體上下抖動'>> <<include '字體上下抖動'>> <</message>> !!!!<<message '15.字體左右抖動'>> <<include '字體左右抖動'>> <</message>> !!!!<<message '15.字體彈出'>> <<include '字體彈出'>> <</message>> !!!!<<message '16.搖擺效果'>> <<include '搖擺效果'>> <</message>> <<return 返回>>
@@color:green; {{{ @@.delayed;第一行@@ @@.delayed;第二行@@ @@.delayed;第三行@@ }}} @@ @@.delayed;第一行@@ @@.delayed;第二行@@ @@.delayed;第三行@@ ---- @@color:green; {{{ <<timed 1s t8n>>第一行文字 <<next>>第二行文字 <<next>>第三行文字 <</timed>> }}} @@ <<timed 1s t8n>>第一行文字 <<next>>第二行文字 <<next>>第三行文字 <</timed>> [[再看一次效果|逐行出現範例]] <<return 返回>>
變數是用於儲存和追蹤遊戲中資訊的資料容器。 這些變數可以包含文字、數字、布林值等不同類型的資料。 在Twine中,允許製作者創建和操作這些變數,以便在遊戲中實現互動性、狀態追蹤和邏輯控制。 * [[變數基本概念]] * [[變數進階:陣列]] * [[變數應用]] <<return 返回>>
陣列的宣告方式請參考[[變數基本概念]],以下為介紹修改與讀取陣列變數數值的語法。 !!!!<<message '1.新增元素'>> * 使用push()方法來新增元素: >> 增加元素:@@color:green;{{{<<run $inventory.push('增加元素')>>}}}@@ >> 增加不重複的元素:@@color:green;{{{<<run $inventory.pushUnique('蘋果')>>}}}@@ → 如果陣列元素已經有蘋果,就不會再增加 * 使用concat()方法把其他變數的元素加入:@@color:green;{{{<<set $inventory to $inventory.concat($chest)>>}}}@@ <</message>> !!!!<<message '2.刪除元素'>> * 使用delete()方法來刪除元素: >> 刪除元素:@@color:green;{{{<<run $inventory.delete('刪除元素')>>}}}@@ >> 刪除第二個元素:@@color:green;{{{<<run $inventory.deleteAt(1)>>}}}@@ >> → 陣列儲存元素的索引值是從0開始計算,第二個元素的索引值為1,所以deleteAt( )中為1。 * 使用pluck()方法來隨機刪除元素: >> 隨機刪除一個元素:@@color:green;{{{<<run $inventory.pluck()>>}}}@@ >> 隨機刪除若干個元素:@@color:green;{{{<<run $inventory.pluckMany(}}}@@3@@color:green;{{{)>>}}}@@ → 3代表刪除3個元素 * 使用pop()方法來刪除最後一個元素:@@color:green;{{{<<run $inventory.pop()>>}}}@@ <</message>> !!!!<<message '3.讀取元素'>> * 使用first()方法回傳第一個元素:@@color:green;{{{<<= $inventory.first()>>}}}@@ → 列印$inventory的第一個元素 * 使用includes()方法查詢是否擁有某元素:@@color:green;{{{<<if $inventory.includes('蘋果')>>}}}@@ → 判斷$inventory陣列中是否有「蘋果」 <</message>> <<return 返回>>
!!!!<<message '1.符合條件下,才執行特定的行動'>> * 搭配"""<<if>>"""巨集使用: >a.假設遇到一扇鎖住的門,擁有鑰匙的情況下,才能開門。 >b.設定鑰匙變數@@color:green;{{{$key}}}@@,初始宣告@@color:green;{{{$key}}}@@為false,當蒐集到鑰匙之後,@@color:green;{{{$key}}}@@變為true。 >c.遇到門的時候,如果@@color:green;{{{$key}}}@@為false,無法開門;如果@@color:green;{{{$key}}}@@為true,可以開門。 >d.變數的[[條件判斷語法參考|https://www.motoslave.net/sugarcube/2/docs/#macros-macro-if]] <table class="border-table"> 【語法內容】 <p>1.在@@StoryInit@@的Passage中宣告變數(預設為沒有鑰匙) @@color:green;{{{<<set $key to false>>}}}@@ 2.在得到鑰匙的Passage設定鑰匙變數為true @@color:green;{{{<<set $key to true>>}}}@@ 3.遇到門的Passage中進行if判定@@color:green; {{{<<if $key>>}}}  門打開了。 {{{<<else>>}}}  無法開門。 {{{<</if>>}}} @@</p></table>語法呈現結果:<<link 有鑰匙 門>><<set $key to true>><</link>>、<<link 沒有鑰匙 門>><<set $key to false>><</link>> <</message>> !!!!<<message '2.隨著故事劇情會變動數值的屬性或物品'>> * 用變數來代表角色某屬性或物品,比如hp、Mp或金錢等。下面以角色血量為例說明。 >a.首先設定角色初始血量為10:在@@StoryInit@@的Passage中宣告血量變數 >>Twine語法:@@color:green;{{{<<set $hp to 10>>}}}@@ >b.當遭遇到會增減血量的事件時,用"""<<set>>"""巨集來更改變數數值。 >>Twine語法:@@color:green;{{{<<set $hp to $hp + 1>>}}}@@ >c.要顯示變數數值內容的話,直接寫出變數"""$hp"""即可。 >>Twine語法:角色血量為@@"""$hp"""@@ >>呈現結果: 角色血量為$hp <</message>> !!!!<<message '3.利用迴圈呈列陣列中的所有元素'>> * 透過迴圈「只出現一次程式碼,卻可能會連續執行多次」的特性,用簡潔的語法來呈列繁雜的資料。以下從「呈列角色身上所有物品」為例來說明。 >a.首先宣告變數儲存角色物品:在@@StoryInit@@的Passage中宣告變數 >>Twine語法:@@color:green;{{{<<set $playerequip to []>>}}}@@ >b.當角色獲得物品時,用"""push()"""方法來新增陣列元素。 >>Twine語法:@@color:green;{{{<<run $playerequip.push('滿級神裝')>>}}}@@ >c.要呈列角色擁有的所有物品,用迴圈語法來處理。 >>Twine語法: >>@@color:green;{{{角色擁有裝備:<<for _i to 0; _i lt $playerequip.length; _i++>>$playerequip[_i]<</for>>}}}@@ <</message>> <<return 返回>>
!!!!<<message '1.輸入框說明'>> 範例:<<textbox '$pie' '預設文字' '連結的PASSAGE'>> 語法:@@color:green;{{{<<textbox '}}}@@@@color:yellow;{{{$pie}}}@@@@color:green;{{{' '}}}@@預設文字@@color:green;{{{' '}}}@@連結的PASSAGE@@color:green;{{{'>>}}}@@ *@@color:yellow;{{{$pie}}}@@:文字框輸入的值會儲存成變數"""$pie"""的值。 *預設文字:文字框預設顯示的內容。 *連結的PASSAGE:按下enter後連結過去的新PASSAGE名稱。 <</message>> !!!!<<message '2.按鈕說明'>> 範例1:<<button '按鈕' '新的passage'>><</button>> 語法:@@color:green;{{{<<button '}}}@@按鈕@@color:green;{{{' '}}}@@新的passage@@color:green;{{{'>><</button>>}}}@@ 或 @@color:green;{{{<<button [[}}}@@按鈕@@color:green;{{{|}}}@@新的passage@@color:green;{{{]]>><</button>>}}}@@ *按鈕:按鈕名稱 *新的passage:點擊後連結過去的新PASSAGE名稱 範例2:<<button [img[media\button.png][新的passage]]>><</button>> 語法:@@color:green;{{{<<button [img[}}}@@media\button.png@@color:green;{{{][}}}@@新的passage@@color:green;{{{]]>><</button>>}}}@@ *button.png:按鈕圖片路徑 *新的passage:點擊後連結過去的新PASSAGE名稱 進階:<<button '按鈕' '新的passage'>><<script>>window.alert("運行JAVASCRIPT程式碼:window.alert();");<</script>><</button>> 點擊按鈕後,先處理特定功能轉入新的PASSAGE 語法:@@color:green;{{{<<button '}}}@@按鈕@@color:green;{{{' '}}}@@新的passage@@color:green;{{{'>>}}}@@@@color:red;{{{<<script>>JAVASCRIPT運行程式碼<</script>>}}}@@@@color:green;{{{<</button>>}}}@@ <</message>> !!!!<<message '3.輸入框與按鈕的結合'>> 範例:輸入正解才能解鎖。 //請輸入密碼解鎖:// <div id="supportercode-form">\ <<textbox '$inputtext' '正解為6' autofocus>> \ <<button '輸入'>> <<set $inputtext to $inputtext.trim()>> <<if $inputtext is '6' >> <<replace "#supportercode-form">>成功解鎖!<</replace>> <<else>> <<replace "#supportercode-error">><br>密碼錯誤,再試一次?<</replace>> <</if>> <</button>>\ <span id="supportercode-error"></span> </div> 語法:@@color:green; {{{ //請輸入密碼解鎖:// <div id='supportercode-form'>\ <<textbox '$inputtext' '輸入框提示詞' autofocus>> \ <<button '輸入'>> <<set $inputtext to $inputtext.trim()>> <<if $inputtext is '密碼正解' >> <<replace '#supportercode-form'>>成功解鎖!<</replace>> <<else>> <<replace '#supportercode-error'>><br>密碼錯誤,再試一次?<</replace>> <</if>> <</button>>\ <span id='supportercode-error'></span> </div> }}} @@ * 輸入框提示詞:選填,為輸入文字的輸入框預設顯示的文字內容。 * 密碼正解:密碼內容。 * 如果希望輸入正解後,自動跳轉到新的PASSAGE,可以把@@color:green;"""<<replace '#supportercode-form'>>成功解鎖!<</replace>>"""@@替換成@@color:green;"""<<goto '新的passage'>>"""@@ * 如果要更動輸入錯誤後的訊息,可以把@@color:green;"""<<replace '#supportercode-error'>><br>密碼錯誤,再試一次?<</replace>>"""@@的「密碼錯誤,再試一次?」改成其他內容。 <</message>> <<return 返回>>
"""$pie"""是@@$pie@@ <<return 返回>>
!!!!1.變數類型 * 故事變數:$+英文名稱 > @@color:green;{{{$var}}}@@ 設置後整個故事都會影響 * 臨時變數:_+英文名稱 > @@color:green;{{{_var}}}@@ 設置後只有在同一Passage頁面中有影響 * 命名限制 > 1. 不能有空格(可以使用大寫或底線 ) > 2. 不能用數字作為開頭 > 3. 不能使用連字號 * 命名建議 > 1. 協作者和未來自己都看得懂變數代表什麼的名稱 > 2. 不好的名稱:test;好的名稱:enemy_name ---- !!!!2.宣告變數 * @@color:red;在使用變數之前,一定要先宣告變數。@@如果設置的變數在故事中經常使用,可以在@@StoryInit@@的Passage中先宣告變數。 * 宣告變數的語法: >> 設置變數的值為數值5:@@color:green;{{{<<set $numberVariable to}}}@@ 5@@color:green;{{{>>}}}@@ >> 設置變數的值為字串:@@color:green;{{{<<set $stringVariable to}}}@@ '字串'@@color:green;{{{>>}}}@@ >> 設置變數的邏輯(true或false):@@color:green;{{{<<set $logicalVariable to}}}@@ true@@color:green;{{{>>}}}@@ >> 設置陣列:@@color:green;{{{<<set $inventory to [}}}@@'盾牌', '胸甲'@@color:green;{{{]>>}}}@@ →@@color:green;{{{[ ]}}}@@中也可以留空,暫不設定陣列元素。 ---- !!!!3.變數運算 * 支援Javascript的[[算術運算符|https://www.motoslave.net/sugarcube/2/docs/#macros-macro-set]] * 要設置變數$apple的數值+1,語法為@@color:green;{{{<<set $apples to $apples + 1>>}}}@@ ---- !!!!4.清除變數值 * 用@@color:green;{{{<<unset>>}}}@@來清除變數值:@@color:green;{{{<<unset $var1, $var2>>}}}@@ <<return 返回>>
用進度條呈現屬性數值或物品數量。 1.使用{{{<progress>}}}元素(進度條),進度條滿值為100。 > 金錢:<progress @value="$money" max="100"></progress> > 語法:@@color:green;{{{<progress @value="}}}@@@@color:red;{{{$money}}}@@@@color:green;{{{" max="}}}@@100@@color:green;{{{"></progress>}}}@@ > 設定@@color:red;{{{$money}}}@@為金錢數量,目前擁有$money。 2.使用{{{<meter>}}}元素(計量條),進度條滿值為100,低於或高於一定值會變色。 > 血量:<span id='hpbar'><meter @value="$hp" min="0" max="100" high="50" low="15"></meter></span> > 語法:@@color:green;{{{<meter @value="}}}@@@@color:red;{{{$hp}}}@@@@color:green;{{{" min="}}}@@0@@color:green;{{{" max="}}}@@100@@color:green;{{{" high="}}}@@@@color:yellow;50@@@@color:green;{{{" low="}}}@@@@color:blue;15@@@@color:green;{{{"></meter>}}}@@ > 設定@@color:red;{{{$hp}}}@@為血量,目前為<span id='hp'>$hp</span>。高於@@color:yellow;50@@、低於@@color:blue;15@@會改變顏色。 > <<button '改血量為40'>><<set $hp to 40>><<replace '#hp'>>$hp<</replace>><<replace '#hpbar'>><meter @value="$hp" min="0" max="100" high="50" low="15"></meter><</replace>><</button>><<button '改血量為10'>><<set $hp to 10>><<replace '#hp'>>$hp<</replace>><<replace '#hpbar'>><meter @value="$hp" min="0" max="100" high="50" low="15"></meter><</replace>><</button>><<button '改血量為80'>><<set $hp to 80>><<replace '#hp'>>$hp<</replace>><<replace '#hpbar'>><meter @value="$hp" min="0" max="100" high="50" low="15"></meter><</replace>><</button>> <<return 返回>>
<<silently>> <<if not $choices>> <<set $choicesCount to -1>> <<set $choices to ["第一個選項", "第二個選項", "第三個選項"]>> <</if>> <<set $choicesCount to $choicesCount + 1>> <<if $choicesCount >= $choices.length>> <<set $choicesCount to 0>> <</if>> <<set $cyclingResult to $choices[$choicesCount]>> <</silently>>\ <<linkreplace $choices[$choicesCount]>><<include "循環選項">><</linkreplace>>
* 新增CSS程式碼 @@color:green; {{{ .delayed { opacity: 0; } }}} @@ * 新增Javascript程式碼 @@color:green; {{{ $(document).on(':passagerender', function (ev) { // Find all elements containing the delayed class. var elems = $(ev.content).find('.delayed'); // Appearance delay (in milliseconds) between each delayed text block. var delay = 1000; // 1 second fade-in if (elems.length > 0) { elems.each(function (i) { $(this) .delay(delay * (i + 1)) .fadeTo(delay, 1); }); } }); }}} @@ * 要逐行出現的文字加上語法 @@color:green;{{{@@.delayed;}}}@@第一行文字@@color:green;{{{@@}}}@@ @@color:green;{{{@@.delayed;}}}@@第二行文字@@color:green;{{{@@}}}@@ @@color:green;{{{@@.delayed;}}}@@第三行文字@@color:green;{{{@@}}}@@ 文字逐行出現[[範例|逐行出現範例]]
<<timed 1s t8n>>1秒後出現文字<</timed>> @@color:green;{{{<<timed 1s t8n>>}}}@@出現的文字內容@@color:green;{{{<</timed>>}}}@@ 要調整出現的時間,更改@@color:green;1s@@(1秒) 不想要文字淡出,刪掉@@color:green;t8n@@ //此語法亦可用來製作逐行出現的效果// 以下是固定@@color:red;一秒@@後,逐行出現各行文字的語法 @@color:green;{{{<<timed 1s t8n>>}}}@@第一行文字 @@color:green;{{{<<next>>}}}@@第二行文字 @@color:green;{{{<<next>>}}}@@第三行文字 @@color:green;{{{<</timed>>}}}@@ 文字逐行出現[[範例|逐行出現範例]]
* 新增CSS程式碼 @@color:green; {{{ .drunk { animation: drunkCam 10s infinite alternate; color: white; } @keyframes drunkCam { 0% { filter: blur(0px); text-shadow: 0 0 0 grey; } 20% { filter: blur(1px); text-shadow: 8px 0 0 grey; } 24% { filter: blur(0px); text-shadow: 0 0 0 grey; } 26% { filter: blur(0px); text-shadow: 0 0 0 grey; } 28% { filter: blur(1px); text-shadow: 10px 0 0 grey; } 30% { filter: blur(0px); text-shadow: 0 0 0 grey; } 60% { filter: blur(1px); text-shadow: 5px 0 0 grey; } 62% { filter: blur(0px); text-shadow: 0 0 0 grey; } 65% { filter: blur(2px); text-shadow: 8px 0 0 grey; } 67% { filter: blur(0px); text-shadow: 0 0 0 grey; } 80% { filter: blur(0px); text-shadow: 8px 0 0 grey; } 85% { filter: blur(2px); text-shadow: 10px 0 0 grey; } 88% { filter: blur(1px); text-shadow: 5px 0 0 grey; } 90% { filter: blur(0px); text-shadow: 0 0 0 grey; } } }}} @@ * 喝醉效果的文字加上語法 第一種語法:@@color:green;{{{<span class="drunk">}}}@@喝醉效果的文字內容。@@color:green;{{{</span>}}}@@ 第二種語法:@@color:green;{{{@@.drunk;}}}@@喝醉效果@@color:green;{{{@@}}}@@ <span class="drunk">喝醉效果的文字內容。</span>
* 新增CSS程式碼 <<message '展示收合程式碼'>> @@color:green; {{{ /* The "clip" in ".glitch" is depreciated, so technically you should use the "clip-path" that's in ".glitchy" instead, however IE doesn't support "clip-path", and most browsers still support "clip". */ .glitch { color: white; position: relative; } .glitch::before { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -9px; text-shadow: 1px 0 blue; top: -5px; color: #FFD; background: #111; overflow: hidden; clip: rect(0, 900px, 0, 0); animation: noise-anim-2 3s infinite linear alternate-reverse; -webkit-animation: noise-anim-2 3s infinite linear alternate-reverse; } .glitch::after { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -5px; text-shadow: -1px 0 red; top: -5px; color: #DFF; background: #111; overflow: hidden; clip: rect(0, 900px, 0, 0); animation: noise-anim 2s infinite linear alternate-reverse; -webkit-animation: noise-anim 2s infinite linear alternate-reverse; } @keyframes noise-anim { 0% { clip: rect(80px, 9999px, 16px, 0); } 5% { clip: rect(94px, 9999px, 17px, 0); } 10% { clip: rect(9px, 9999px, 92px, 0); } 15.0% { clip: rect(37px, 9999px, 4px, 0); } 20% { clip: rect(53px, 9999px, 47px, 0); } 25% { clip: rect(99px, 9999px, 69px, 0); } 30.0% { clip: rect(15px, 9999px, 49px, 0); } 35% { clip: rect(83px, 9999px, 81px, 0); } 40% { clip: rect(55px, 9999px, 43px, 0); } 45% { clip: rect(28px, 9999px, 27px, 0); } 50% { clip: rect(64px, 9999px, 28px, 0); } 55.0% { clip: rect(76px, 9999px, 91px, 0); } 60.0% { clip: rect(29px, 9999px, 72px, 0); } 65% { clip: rect(70px, 9999px, 10px, 0); } 70% { clip: rect(5px, 9999px, 84px, 0); } 75% { clip: rect(46px, 9999px, 81px, 0); } 80% { clip: rect(7px, 9999px, 85px, 0); } 85.0% { clip: rect(17px, 9999px, 89px, 0); } 90% { clip: rect(41px, 9999px, 59px, 0); } 95% { clip: rect(53px, 9999px, 90px, 0); } 100% { clip: rect(10px, 9999px, 30px, 0); } } @keyframes noise-anim-2 { 0% { clip: rect(84px, 9999px, 3px, 0); } 5% { clip: rect(31px, 9999px, 69px, 0); } 10% { clip: rect(37px, 9999px, 33px, 0); } 15.0% { clip: rect(18px, 9999px, 6px, 0); } 20% { clip: rect(25px, 9999px, 87px, 0); } 25% { clip: rect(100px, 9999px, 8px, 0); } 30.0% { clip: rect(24px, 9999px, 87px, 0); } 35% { clip: rect(39px, 9999px, 16px, 0); } 40% { clip: rect(96px, 9999px, 25px, 0); } 45% { clip: rect(16px, 9999px, 100px, 0); } 50% { clip: rect(92px, 9999px, 76px, 0); } 55.0% { clip: rect(29px, 9999px, 40px, 0); } 60.0% { clip: rect(40px, 9999px, 39px, 0); } 65% { clip: rect(94px, 9999px, 44px, 0); } 70% { clip: rect(2px, 9999px, 78px, 0); } 75% { clip: rect(86px, 9999px, 50px, 0); } 80% { clip: rect(2px, 9999px, 46px, 0); } 85.0% { clip: rect(41px, 9999px, 71px, 0); } 90% { clip: rect(75px, 9999px, 15px, 0); } 95% { clip: rect(41px, 9999px, 8px, 0); } 100% { clip: rect(23px, 9999px, 75px, 0); } } .glitchy { color: white; position: relative; } .glitchy::before { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -9px; text-shadow: 1px 0 blue; top: -5px; color: #FFD; background: #111; overflow: hidden; clip-path: polygon(0 0); animation: noise-animY-2 3s infinite linear alternate-reverse; -webkit-animation: noise-animY-2 3s infinite linear alternate-reverse; } .glitchy::after { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -5px; text-shadow: -1px 0 red; top: -5px; color: #DFF; background: #111; overflow: hidden; clip-path: polygon(0 0); animation: noise-animY 2s infinite linear alternate-reverse; -webkit-animation: noise-animY 2s infinite linear alternate-reverse; } @keyframes noise-animY { 0% { clip-path: polygon(0 80px, 100% 80px, 100% 16px, 0 16px); } 5% { clip-path: polygon(0 94px, 100% 94px, 100% 17px, 0 17px); } 10% { clip-path: polygon(0 9px, 100% 9px, 100% 92px, 0 92px); } 15.0% { clip-path: polygon(0 37px, 100% 37px, 100% 4px, 0 4px); } 20% { clip-path: polygon(0 53px, 100% 53px, 100% 47px, 0 47px); } 25% { clip-path: polygon(0 99px, 100% 99px, 100% 69px, 0 69px); } 30.0% { clip-path: polygon(0 15px, 100% 15px, 100% 49px, 0 49px); } 35% { clip-path: polygon(0 83px, 100% 83px, 100% 81px, 0 81px); } 40% { clip-path: polygon(0 55px, 100% 55px, 100% 43px, 0 43px); } 45% { clip-path: polygon(0 28px, 100% 28px, 100% 27px, 0 27px); } 50% { clip-path: polygon(0 64px, 100% 64px, 100% 28px, 0 28px); } 55.0% { clip-path: polygon(0 76px, 100% 76px, 100% 91px, 0 91px); } 60.0% { clip-path: polygon(0 29px, 100% 29px, 100% 72px, 0 72px); } 65% { clip-path: polygon(0 70px, 100% 70px, 100% 10px, 0 10px); } 70% { clip-path: polygon(0 5px, 100% 5px, 100% 84px, 0 84px); } 75% { clip-path: polygon(0 46px, 100% 46px, 100% 81px, 0 81px); } 80% { clip-path: polygon(0 7px, 100% 7px, 100% 85px, 0 85px); } 85.0% { clip-path: polygon(0 17px, 100% 17px, 100% 89px, 0 89px); } 90% { clip-path: polygon(0 41px, 100% 41px, 100% 59px, 0 59px); } 95% { clip-path: polygon(0 53px, 100% 53px, 100% 90px, 0 90px); } 100% { clip-path: polygon(0 10px, 100% 10px, 100% 30px, 0 30px); } } @keyframes noise-animY-2 { 0% { clip-path: polygon(0 84px, 100% 84px, 100% 3px, 0 3px); } 5% { clip-path: polygon(0 31px, 100% 31px, 100% 69px, 0 69px); } 10% { clip-path: polygon(0 37px, 100% 37px, 100% 33px, 0 33px); } 15.0% { clip-path: polygon(0 18px, 100% 18px, 100% 6px, 0 6px); } 20% { clip-path: polygon(0 25px, 100% 25px, 100% 87px, 0 87px); } 25% { clip-path: polygon(0 100px, 100% 100px, 100% 8px, 0 8px); } 30.0% { clip-path: polygon(0 24px, 100% 24px, 100% 87px, 0 87px); } 35% { clip-path: polygon(0 39px, 100% 39px, 100% 16px, 0 16px); } 40% { clip-path: polygon(0 96px, 100% 96px, 100% 25px, 0 25px); } 45% { clip-path: polygon(0 16px, 100% 16px, 100% 100px, 0 100px); } 50% { clip-path: polygon(0 92px, 100% 92px, 100% 76px, 0 76px); } 55.0% { clip-path: polygon(0 29px, 100% 29px, 100% 40px, 0 40px); } 60.0% { clip-path: polygon(0 40px, 100% 40px, 100% 39px, 0 39px); } 65% { clip-path: polygon(0 94px, 100% 94px, 100% 44px, 0 44px); } 70% { clip-path: polygon(0 2px, 100% 2px, 100% 78px, 0 78px); } 75% { clip-path: polygon(0 86px, 100% 86px, 100% 50px, 0 50px); } 80% { clip-path: polygon(0 2px, 100% 2px, 100% 46px, 0 46px); } 85.0% { clip-path: polygon(0 41px, 100% 41px, 100% 71px, 0 71px); } 90% { clip-path: polygon(0 75px, 100% 75px, 100% 15px, 0 15px); } 95% { clip-path: polygon(0 41px, 100% 41px, 100% 8px, 0 8px); } 100% { clip-path: polygon(0 23px, 100% 23px, 100% 75px, 0 75px); } } }}} @@<</message>> * 發抖效果的文字加上語法 @@color:green;{{{<span class="glitchy" data-text="}}}@@發抖效果的文字內容。@@color:green;{{{">}}}@@發抖效果的文字內容。@@color:green;{{{</span>}}}@@ <span class="glitchy" data-text="抖動效果文字的內容。">發抖效果文字的內容。</span> 只有單一文字@@color:green;{{{<span class="glitchy" data-text="}}}@@恐怖@@color:green;{{{">}}}@@發抖@@color:green;{{{</span>。}}}@@ 只有單一文字<span class="glitchy" data-text="恐怖">發抖</span>。
* 新增CSS程式碼 @@color:green; {{{ .shadow { text-shadow: 0.08em 0.08em 0.08em #FFF; } }}} @@ * 加上語法 @@color:green;{{{@@.shadow;}}}@@文字陰影效果@@color:green;{{{@@}}}@@ @@.shadow;文字陰影效果@@
* 新增CSS程式碼 @@color:green; {{{ .emboss { text-shadow: 0.08em 0.08em 0em #FFF; color: black; } }}} @@ * 加上語法 @@color:green;{{{@@.emboss;}}}@@浮凸字體效果@@color:green;{{{@@}}}@@ @@.emboss;浮凸字體效果@@
* 新增CSS程式碼 @@color:green; {{{ .blur { color: transparent; text-shadow: 0em 0em 0.08em #FFF; } }}} @@ * 加上語法 @@color:green;{{{@@.blur;}}}@@模糊字體效果@@color:green;{{{@@}}}@@ @@.blur;模糊字體效果@@
* 新增CSS程式碼 @@color:green; {{{ .blurrier { color: transparent; text-shadow: 0em 0em 0.2em #FFF; } .blurrier::selection { background-color: transparent; color: transparent; } .blurrier::-moz-selection { background-color: transparent; color: transparent; } }}} @@ * 加上語法 @@color:green;{{{@@.blurrier;}}}@@焦糊字體效果@@color:green;{{{@@}}}@@ @@.blurrier;焦糊字體效果@@
* 新增CSS程式碼 @@color:green; {{{ .smear { color: transparent; text-shadow: 0em 0em 0.02em rgba(255,255,255,0.75), -0.2em 0em 0.5em rgba(255,255,255,0.5), 0.2em 0em 0.5em rgba(255,255,255,0.5); } }}} @@ * 加上語法 @@color:green;{{{@@.smear;}}}@@光暈字體效果@@color:green;{{{@@}}}@@ @@.smear;光暈字體效果@@
* 新增CSS程式碼 @@color:green; {{{ .mirror { display: inline-block; transform: scaleX(-1); -webkit-transform: scaleX(-1); } }}} @@ * 加上語法 @@color:green;{{{@@.mirror;}}}@@字體鏡射效果@@color:green;{{{@@}}}@@ @@.mirror;字體鏡射效果@@
* 新增CSS程式碼 @@color:green; {{{ .upside-down { display: inline-block; transform: scaleY(-1); -webkit-transform: scaleY(-1); } }}} @@ * 加上語法 @@color:green;{{{@@.upside-down;}}}@@字體上下顛倒效果@@color:green;{{{@@}}}@@ @@.upside-down;字體上下顛倒效果@@
* 新增CSS程式碼 @@color:green; {{{ @-webkit-keyframes rumble { 50% { -webkit-transform: translateY(-.2em); transform: translateY(-.2em) } } @keyframes rumble { 50% { -webkit-transform: translateY(-.2em); transform: translateY(-.2em) } } .rumble { -webkit-animation: rumble linear 0.1s 0s infinite; animation: rumble linear 0.1s 0s infinite; display:inline-block; } }}} @@ * 加上語法 @@color:green;{{{@@.rumble;}}}@@字體上下抖動效果@@color:green;{{{@@}}}@@ @@.rumble;字體上下抖動效果@@
* 新增CSS程式碼 @@color:green; {{{ @-webkit-keyframes shudder { 50% { -webkit-transform: translateX(0.2em); transform: translateX(0.2em) } } @keyframes shudder { 50% { -webkit-transform: translateX(0.2em); transform: translateX(0.2em) } } .shudder { -webkit-animation: shudder linear 0.1s 0s infinite; animation: shudder linear 0.1s 0s infinite; display: inline-block; } }}} @@ * 加上語法 @@color:green;{{{@@.shudder;}}}@@字體左右抖動效果@@color:green;{{{@@}}}@@ @@.shudder;字體左右抖動效果@@
* 加上語法 @@color:green;{{{@@.pulse;}}}@@字體彈出效果@@color:green;{{{@@}}}@@ @@.pulse;字體彈出效果@@ * 新增CSS程式碼 @@color:green; {{{ @-webkit-keyframes pulse { 0% { -webkit-transform: scale(0, 0); transform: scale(0, 0) } 20% { -webkit-transform: scale(1.2, 1.2); transform: scale(1.2, 1.2) } 40% { -webkit-transform: scale(0.9, 0.9); transform: scale(0.9, 0.9) } 60% { -webkit-transform: scale(1.05, 1.05); transform: scale(1.05, 1.05) } 80% { -webkit-transform: scale(0.925, 0.925); transform:scale(0.925, 0.925) } to { -webkit-transform: scale(1, 1); transform: scale(1, 1) } } @keyframes pulse { 0% { -webkit-transform: scale(0, 0); transform: scale(0, 0) } 20% { -webkit-transform: scale(1.2, 1.2); transform: scale(1.2, 1.2) } 40% { -webkit-transform: scale(0.9, 0.9); transform: scale(0.9, 0.9) } 60% { -webkit-transform: scale(1.05, 1.05); transform: scale(1.05, 1.05) } 80% { -webkit-transform: scale(0.925, 0.925); transform: scale(0.925, 0.925) } to { -webkit-transform: scale(1, 1); transform: scale(1, 1) } } .pulse { -webkit-animation: pulse 0.8s; animation: pulse 0.8s; display: inline-block; } }}} @@
* 新增CSS程式碼 @@color:green; {{{ @-webkit-keyframes fade-in-out { 0%, to {opacity: 0} 50% {opacity: 1} } @keyframes fade-in-out { 0%, to {opacity: 0} 50% {opacity: 1} } .fade-in-out { text-decoration: none; animation: fade-in-out 2s ease-in-out infinite alternate; -webkit-animation: fade-in-out 2s ease-in-out infinite alternate; } }}} @@ * 加上語法 @@color:green;{{{@@.fade-in-out;}}}@@字體閃爍效果@@color:green;{{{@@}}}@@ @@.fade-in-out;字體閃爍效果@@
<h3>@@JAVASCRIPT@@</h3> {{{ <!-- 動態效果:.delayed --> $(document).on(':passagerender', function (ev) { // Find all elements containing the delayed class. var elems = $(ev.content).find('.delayed'); // Appearance delay (in milliseconds) between each delayed text block. var delay = 1000; // 1 second fade-in if (elems.length > 0) { elems.each(function (i) { $(this) .delay(delay * (i + 1)) .fadeTo(delay, 1); }); } }); <!-- 動態效果:<<shake>> --> !function(){"use strict";if("undefined"==typeof version||"undefined"==typeof version.title||"SugarCube"!==version.title||"undefined"==typeof version.major||version.major<2||"undefined"==typeof version.minor||version.minor<5)throw new Error("<<shake>> macro requires SugarCube 2.5.0 or greater, aborting load");Macro.add("shake",{tags:null,handler:function(){var duration=this.args.length>0?this.args[0]:1/0,shakeClass="shake";if(1/0!==duration)try{duration=Math.max(Engine.minDomActionDelay,Util.fromCssTime(duration))}catch(e){return this.error(e.message)}Config.debug&&this.debugView.modes({block:!0});var $wrapper=jQuery(document.createElement("span"));$wrapper.addClass("macro-"+this.name+" "+shakeClass).wiki(this.payload[0].contents).appendTo(this.output),1/0!==duration&&setTimeout(function(){$wrapper.removeClass(shakeClass)},Engine.minDomActionDelay+duration)}}),Macro.add(["shakescreen","shaketarget"],{handler:function(){var $targets,duration,shakeClass;if("shakescreen"===this.name)$targets=jQuery("#passages"),duration=this.args.length>0?this.args[0]:1/0,shakeClass="shake-block";else{if(0===this.args.length)return this.error("no selector specified");if($targets=jQuery(this.args[0]),0===$targets.length)return this.error('no elements matched the selector "'+this.args[0]+'"');duration=this.args.length>1?this.args[1]:1/0,shakeClass="block"===jQuery($targets[0]).css("display")?"shake-block":"shake"}if("stop"===duration)return void $targets.removeClass(shakeClass);if(1/0!==duration)try{duration=Math.max(Engine.minDomActionDelay,Util.fromCssTime(duration))}catch(e){return this.error(e.message)}$targets.addClass(shakeClass),1/0!==duration&&setTimeout(function(){$targets.removeClass(shakeClass)},Engine.minDomActionDelay+duration)}})}(); }}} ---- <h3>@@CSS@@</h3> {{{ /* 文字置中 */ .center { text-align: center; } /* 逐行出現 */ .delayed { opacity: 0; } /* 文字陰影 */ .shadow { text-shadow: 0.08em 0.08em 0.08em #FFF; } /* 文字浮凸 */ .emboss { text-shadow: 0.08em 0.08em 0em #FFF; color: black; } /* 文字模糊 */ .blur { color: transparent; text-shadow: 0em 0em 0.08em #FFF; } /* 文字焦糊 */ .blurrier { color: transparent; text-shadow: 0em 0em 0.2em #FFF; } .blurrier::selection { background-color: transparent; color: transparent; } .blurrier::-moz-selection { background-color: transparent; color: transparent; } /* 光暈效果 */ .smear { color: transparent; text-shadow: 0em 0em 0.02em rgba(255,255,255,0.75), -0.2em 0em 0.5em rgba(255,255,255,0.5), 0.2em 0em 0.5em rgba(255,255,255,0.5); } /* 文字鏡射 */ .mirror { display: inline-block; transform: scaleX(-1); -webkit-transform: scaleX(-1); } /* 文字上下顛倒 */ .upside-down { display: inline-block; transform: scaleY(-1); -webkit-transform: scaleY(-1); } /* 閃爍效果 */ @-webkit-keyframes fade-in-out { 0%, to {opacity: 0} 50% {opacity: 1} } @keyframes fade-in-out { 0%, to {opacity: 0} 50% {opacity: 1} } .fade-in-out { text-decoration: none; animation: fade-in-out 2s ease-in-out infinite alternate; -webkit-animation: fade-in-out 2s ease-in-out infinite alternate; } /* 上下抖動 */ @-webkit-keyframes rumble { 50% { -webkit-transform: translateY(-.2em); transform: translateY(-.2em) } } @keyframes rumble { 50% { -webkit-transform: translateY(-.2em); transform: translateY(-.2em) } } .rumble { -webkit-animation: rumble linear 0.1s 0s infinite; animation: rumble linear 0.1s 0s infinite; display:inline-block; } /* 左右抖動 */ @-webkit-keyframes shudder { 50% { -webkit-transform: translateX(0.2em); transform: translateX(0.2em) } } @keyframes shudder { 50% { -webkit-transform: translateX(0.2em); transform: translateX(0.2em) } } .shudder { -webkit-animation: shudder linear 0.1s 0s infinite; animation: shudder linear 0.1s 0s infinite; display: inline-block; } /* 彈出效果 */ @-webkit-keyframes pulse { 0% { -webkit-transform: scale(0, 0); transform: scale(0, 0) } 20% { -webkit-transform: scale(1.2, 1.2); transform: scale(1.2, 1.2) } 40% { -webkit-transform: scale(0.9, 0.9); transform: scale(0.9, 0.9) } 60% { -webkit-transform: scale(1.05, 1.05); transform: scale(1.05, 1.05) } 80% { -webkit-transform: scale(0.925, 0.925); transform:scale(0.925, 0.925) } to { -webkit-transform: scale(1, 1); transform: scale(1, 1) } } @keyframes pulse { 0% { -webkit-transform: scale(0, 0); transform: scale(0, 0) } 20% { -webkit-transform: scale(1.2, 1.2); transform: scale(1.2, 1.2) } 40% { -webkit-transform: scale(0.9, 0.9); transform: scale(0.9, 0.9) } 60% { -webkit-transform: scale(1.05, 1.05); transform: scale(1.05, 1.05) } 80% { -webkit-transform: scale(0.925, 0.925); transform: scale(0.925, 0.925) } to { -webkit-transform: scale(1, 1); transform: scale(1, 1) } } .pulse { -webkit-animation: pulse 0.8s; animation: pulse 0.8s; display: inline-block; } /* 喝醉效果 */ .drunk { animation: drunkCam 10s infinite alternate; color: white; } @keyframes drunkCam { 0% { filter: blur(0px); text-shadow: 0 0 0 grey; } 20% { filter: blur(1px); text-shadow: 8px 0 0 grey; } 24% { filter: blur(0px); text-shadow: 0 0 0 grey; } 26% { filter: blur(0px); text-shadow: 0 0 0 grey; } 28% { filter: blur(1px); text-shadow: 10px 0 0 grey; } 30% { filter: blur(0px); text-shadow: 0 0 0 grey; } 60% { filter: blur(1px); text-shadow: 5px 0 0 grey; } 62% { filter: blur(0px); text-shadow: 0 0 0 grey; } 65% { filter: blur(2px); text-shadow: 8px 0 0 grey; } 67% { filter: blur(0px); text-shadow: 0 0 0 grey; } 80% { filter: blur(0px); text-shadow: 8px 0 0 grey; } 85% { filter: blur(2px); text-shadow: 10px 0 0 grey; } 88% { filter: blur(1px); text-shadow: 5px 0 0 grey; } 90% { filter: blur(0px); text-shadow: 0 0 0 grey; } } /* 發抖效果 */ /* The "clip" in ".glitch" is depreciated, so technically you should use the "clip-path" that's in ".glitchy" instead, however IE doesn't support "clip-path", and most browsers still support "clip". */ .glitch { color: white; position: relative; } .glitch:before { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -9px; text-shadow: 1px 0 blue; top: -5px; color: #FFD; background: #111; overflow: hidden; clip: rect(0, 900px, 0, 0); animation: noise-anim-2 3s infinite linear alternate-reverse; -webkit-animation: noise-anim-2 3s infinite linear alternate-reverse; } .glitch:after { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -5px; text-shadow: -1px 0 red; top: -5px; color: #DFF; background: #111; overflow: hidden; clip: rect(0, 900px, 0, 0); animation: noise-anim 2s infinite linear alternate-reverse; -webkit-animation: noise-anim 2s infinite linear alternate-reverse; } @keyframes noise-anim { 0% { clip: rect(80px, 9999px, 16px, 0); } 5% { clip: rect(94px, 9999px, 17px, 0); } 10% { clip: rect(9px, 9999px, 92px, 0); } 15.0% { clip: rect(37px, 9999px, 4px, 0); } 20% { clip: rect(53px, 9999px, 47px, 0); } 25% { clip: rect(99px, 9999px, 69px, 0); } 30.0% { clip: rect(15px, 9999px, 49px, 0); } 35% { clip: rect(83px, 9999px, 81px, 0); } 40% { clip: rect(55px, 9999px, 43px, 0); } 45% { clip: rect(28px, 9999px, 27px, 0); } 50% { clip: rect(64px, 9999px, 28px, 0); } 55.0% { clip: rect(76px, 9999px, 91px, 0); } 60.0% { clip: rect(29px, 9999px, 72px, 0); } 65% { clip: rect(70px, 9999px, 10px, 0); } 70% { clip: rect(5px, 9999px, 84px, 0); } 75% { clip: rect(46px, 9999px, 81px, 0); } 80% { clip: rect(7px, 9999px, 85px, 0); } 85.0% { clip: rect(17px, 9999px, 89px, 0); } 90% { clip: rect(41px, 9999px, 59px, 0); } 95% { clip: rect(53px, 9999px, 90px, 0); } 100% { clip: rect(10px, 9999px, 30px, 0); } } @keyframes noise-anim-2 { 0% { clip: rect(84px, 9999px, 3px, 0); } 5% { clip: rect(31px, 9999px, 69px, 0); } 10% { clip: rect(37px, 9999px, 33px, 0); } 15.0% { clip: rect(18px, 9999px, 6px, 0); } 20% { clip: rect(25px, 9999px, 87px, 0); } 25% { clip: rect(100px, 9999px, 8px, 0); } 30.0% { clip: rect(24px, 9999px, 87px, 0); } 35% { clip: rect(39px, 9999px, 16px, 0); } 40% { clip: rect(96px, 9999px, 25px, 0); } 45% { clip: rect(16px, 9999px, 100px, 0); } 50% { clip: rect(92px, 9999px, 76px, 0); } 55.0% { clip: rect(29px, 9999px, 40px, 0); } 60.0% { clip: rect(40px, 9999px, 39px, 0); } 65% { clip: rect(94px, 9999px, 44px, 0); } 70% { clip: rect(2px, 9999px, 78px, 0); } 75% { clip: rect(86px, 9999px, 50px, 0); } 80% { clip: rect(2px, 9999px, 46px, 0); } 85.0% { clip: rect(41px, 9999px, 71px, 0); } 90% { clip: rect(75px, 9999px, 15px, 0); } 95% { clip: rect(41px, 9999px, 8px, 0); } 100% { clip: rect(23px, 9999px, 75px, 0); } } .glitchy { color: white; position: relative; } .glitchy:before { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -9px; text-shadow: 1px 0 blue; top: -5px; color: #FFD; background: #111; overflow: hidden; clip-path: polygon(0 0); animation: noise-animY-2 3s infinite linear alternate-reverse; -webkit-animation: noise-animY-2 3s infinite linear alternate-reverse; } .glitchy:after { content: attr(data-text); width: calc(100% + 0.5em); position: absolute; left: -5px; text-shadow: -1px 0 red; top: -5px; color: #DFF; background: #111; overflow: hidden; clip-path: polygon(0 0); animation: noise-animY 2s infinite linear alternate-reverse; -webkit-animation: noise-animY 2s infinite linear alternate-reverse; } @keyframes noise-animY { 0% { clip-path: polygon(0 80px, 100% 80px, 100% 16px, 0 16px); } 5% { clip-path: polygon(0 94px, 100% 94px, 100% 17px, 0 17px); } 10% { clip-path: polygon(0 9px, 100% 9px, 100% 92px, 0 92px); } 15.0% { clip-path: polygon(0 37px, 100% 37px, 100% 4px, 0 4px); } 20% { clip-path: polygon(0 53px, 100% 53px, 100% 47px, 0 47px); } 25% { clip-path: polygon(0 99px, 100% 99px, 100% 69px, 0 69px); } 30.0% { clip-path: polygon(0 15px, 100% 15px, 100% 49px, 0 49px); } 35% { clip-path: polygon(0 83px, 100% 83px, 100% 81px, 0 81px); } 40% { clip-path: polygon(0 55px, 100% 55px, 100% 43px, 0 43px); } 45% { clip-path: polygon(0 28px, 100% 28px, 100% 27px, 0 27px); } 50% { clip-path: polygon(0 64px, 100% 64px, 100% 28px, 0 28px); } 55.0% { clip-path: polygon(0 76px, 100% 76px, 100% 91px, 0 91px); } 60.0% { clip-path: polygon(0 29px, 100% 29px, 100% 72px, 0 72px); } 65% { clip-path: polygon(0 70px, 100% 70px, 100% 10px, 0 10px); } 70% { clip-path: polygon(0 5px, 100% 5px, 100% 84px, 0 84px); } 75% { clip-path: polygon(0 46px, 100% 46px, 100% 81px, 0 81px); } 80% { clip-path: polygon(0 7px, 100% 7px, 100% 85px, 0 85px); } 85.0% { clip-path: polygon(0 17px, 100% 17px, 100% 89px, 0 89px); } 90% { clip-path: polygon(0 41px, 100% 41px, 100% 59px, 0 59px); } 95% { clip-path: polygon(0 53px, 100% 53px, 100% 90px, 0 90px); } 100% { clip-path: polygon(0 10px, 100% 10px, 100% 30px, 0 30px); } } @keyframes noise-animY-2 { 0% { clip-path: polygon(0 84px, 100% 84px, 100% 3px, 0 3px); } 5% { clip-path: polygon(0 31px, 100% 31px, 100% 69px, 0 69px); } 10% { clip-path: polygon(0 37px, 100% 37px, 100% 33px, 0 33px); } 15.0% { clip-path: polygon(0 18px, 100% 18px, 100% 6px, 0 6px); } 20% { clip-path: polygon(0 25px, 100% 25px, 100% 87px, 0 87px); } 25% { clip-path: polygon(0 100px, 100% 100px, 100% 8px, 0 8px); } 30.0% { clip-path: polygon(0 24px, 100% 24px, 100% 87px, 0 87px); } 35% { clip-path: polygon(0 39px, 100% 39px, 100% 16px, 0 16px); } 40% { clip-path: polygon(0 96px, 100% 96px, 100% 25px, 0 25px); } 45% { clip-path: polygon(0 16px, 100% 16px, 100% 100px, 0 100px); } 50% { clip-path: polygon(0 92px, 100% 92px, 100% 76px, 0 76px); } 55.0% { clip-path: polygon(0 29px, 100% 29px, 100% 40px, 0 40px); } 60.0% { clip-path: polygon(0 40px, 100% 40px, 100% 39px, 0 39px); } 65% { clip-path: polygon(0 94px, 100% 94px, 100% 44px, 0 44px); } 70% { clip-path: polygon(0 2px, 100% 2px, 100% 78px, 0 78px); } 75% { clip-path: polygon(0 86px, 100% 86px, 100% 50px, 0 50px); } 80% { clip-path: polygon(0 2px, 100% 2px, 100% 46px, 0 46px); } 85.0% { clip-path: polygon(0 41px, 100% 41px, 100% 71px, 0 71px); } 90% { clip-path: polygon(0 75px, 100% 75px, 100% 15px, 0 15px); } 95% { clip-path: polygon(0 41px, 100% 41px, 100% 8px, 0 8px); } 100% { clip-path: polygon(0 23px, 100% 23px, 100% 75px, 0 75px); } } /*! <<shake>> */ @-webkit-keyframes shakeanim { 0% { -webkit-transform: translate(2px, 1px) rotate(0deg); } 10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); } 20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); } 30% { -webkit-transform: translate(0px, 2px) rotate(0deg); } 40% { -webkit-transform: translate(1px, -1px) rotate(1deg); } 50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); } 60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); } 70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); } 80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); } 90% { -webkit-transform: translate(2px, 2px) rotate(0deg); } 100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); } } @-o-keyframes shakeanim { 0% { -o-transform: translate(2px, 1px) rotate(0deg); } 10% { -o-transform: translate(-1px, -2px) rotate(-1deg); } 20% { -o-transform: translate(-3px, 0px) rotate(1deg); } 30% { -o-transform: translate(0px, 2px) rotate(0deg); } 40% { -o-transform: translate(1px, -1px) rotate(1deg); } 50% { -o-transform: translate(-1px, 2px) rotate(-1deg); } 60% { -o-transform: translate(-3px, 1px) rotate(0deg); } 70% { -o-transform: translate(2px, 1px) rotate(-1deg); } 80% { -o-transform: translate(-1px, -1px) rotate(1deg); } 90% { -o-transform: translate(2px, 2px) rotate(0deg); } 100% { -o-transform: translate(1px, -2px) rotate(-1deg); } } @keyframes shakeanim { 0% { transform: translate(2px, 1px) rotate(0deg); } 10% { transform: translate(-1px, -2px) rotate(-1deg); } 20% { transform: translate(-3px, 0px) rotate(1deg); } 30% { transform: translate(0px, 2px) rotate(0deg); } 40% { transform: translate(1px, -1px) rotate(1deg); } 50% { transform: translate(-1px, 2px) rotate(-1deg); } 60% { transform: translate(-3px, 1px) rotate(0deg); } 70% { transform: translate(2px, 1px) rotate(-1deg); } 80% { transform: translate(-1px, -1px) rotate(1deg); } 90% { transform: translate(2px, 2px) rotate(0deg); } 100% { transform: translate(1px, -2px) rotate(-1deg); } } .shake-block, .shake { -webkit-animation-duration: 0.8s; -o-animation-duration: 0.8s; animation-duration: 0.8s; -webkit-animation-iteration-count: infinite; -o-animation-iteration-count: infinite; animation-iteration-count: infinite; -webkit-animation-name: shakeanim; -o-animation-name: shakeanim; animation-name: shakeanim; -webkit-animation-timing-function: linear; -o-animation-timing-function: linear; animation-timing-function: linear; -webkit-transform-origin: 50% 50%; -ms-transform-origin: 50% 50%; -o-transform-origin: 50% 50%; transform-origin: 50% 50%; } .shake-block { display: block; } .shake { display: inline-block; } }}} <<return 返回>>
@@.textbold;<h1>文字加粗</h1>@@ * 新增CSS程式碼 @@color:grey; {{{ .textbold { -webkit-text-stroke: 1px white; color: white; } }}} @@ * 加上語法 @@color:green;{{{@@.textbold;<h1>}}}@@文字加粗@@color:green;{{{</h1>@@}}}@@ ---- <div style="background-color:red;width:30%;">@@.textframe;<h1>文字加框</h1>@@</div> * 新增CSS程式碼 @@color:grey; {{{ .textframe { -webkit-text-stroke: 1px white; color: black; } }}} @@ * 加上語法 @@color:green;{{{@@.textframe;<h1>}}}@@文字加框@@color:green;{{{</h1>@@}}}@@ ---- <div style="background-color:red;width:30%;">@@.texthollow;<h1>文字鏤空</h1>@@</div> * 新增CSS程式碼 @@color:grey; {{{ .texthollow { -webkit-text-stroke: 1px white; color: transparent; } }}} @@ * 加上語法 @@color:green;{{{@@.texthollow;<h1>}}}@@文字鏤空@@color:green;{{{</h1>@@}}}@@ <<return 返回>>
使用Animate.css的圖片 <div align='left'><img data-Passage ="start" class ="animate__animated animate__bounce animate__slow animate__infinite" src="media/img/線索3.png" width="30%"></div> <div align='center'><img data-Passage ="start" class ="animate__animated animate__bounce animate__infinite" src="media/img/線索3.png" width="30%"></div> <div align='right'><img data-Passage ="start" class ="animate__animated animate__bounce animate__fast animate__infinite" src="media/img/線索3.png" width="30%"></div> <h2>使用說明</h2> 1. 匯入外部CSS語法([[匯入方法說明|CSS使用說明]]) @@color:green;{{{importStyles("https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css")}}}@@ 2. 在<img>圖片元素中新增Animate.css的class屬性: @@color:green;{{{<img }}}@@@@color:red;{{{class ="animate__animated animate__bounce animate__delay-1s animate__repeat-2"}}}@@@@color:green;{{{ src="media/線索3.png" width="30%">}}}@@ class屬性的格式為:{{{animate__XXXXXX → XXXXXX}}}替換成要使用的動畫名稱。 例如: * {{{animate__animated}}} → 啟用動畫 * {{{animate__delay-2s}}} → 延遲兩秒 * {{{animate__repeat-2}}} → 重複兩次 * {{{animate__infinite}}} → 無限循環 * {{{animate__slow}}} → 動畫變慢(變成2秒) * {{{animate__fast}}} → 動畫變快(變成800毫秒) * {{{animate__faster}}} → 動畫變超快(變成500毫秒)  動畫的速度默認為1秒 [[Animate.css中文說明|https://hsuchihting.github.io/jQuery/20200713/60669434/]] <<return 返回>>
因為Twine開發的遊戲為html格式,支援CSS語法。 故事文本的文字樣式、對齊方式,都可以透過CSS語法來設定。 要怎麼使用CSS,基本觀念可以參考[[此文章說明|https://intfiction.org/t/scaling-images-to-fit/46165/2]] CSS在twine中不需要再透過特殊語法來控制,可直接沿用html、CSS原本的語法來設定。 詳細可參考[[MDN文件說明|https://developer.mozilla.org/zh-TW/docs/Web/CSS]] 如果你不喜歡Twine的預設版面,你可以在"StoryInterface"的Passage中重設UI。 * 關於sugarcube版面使用的markup可以參考[[此文件|http://www.motoslave.net/sugarcube/2/docs/html.html]] * 關於sugarcube版面的預設CSS可以參考[[此文件|https://www.motoslave.net/sugarcube/2/docs/#css]] <<return 返回>>
使用sugarcube巨集@@color:green;{{{<<listbox>>}}}@@,語法格式如下: @@color:green; {{{ <<listbox receiverName [autoselect]>> [<<option label [value [selected]]>> …] [<<optionsfrom collection>> …] <</listbox>> }}} @@ # @@color:green;receiverName@@:用來儲存選項數值的變數名稱,變數前後必須加上@@color:green;{{{" "}}}@@,例如@@color:green;{{{"$foo"}}}@@。也支援物件(Object)和陣列(Array),例如@@color:green;{{{"$foo.bar"}}}@@、@@color:green;{{{"$foo['bar']"}}}@@;@@color:green;{{{"$foo[0]"}}}@@ # @@color:green;autoselect@@:選填,用來顯示輸入框的預設選項,以第一個選項為預設選項。 # @@color:green;{{{<<option>>/<<optionsfrom>>}}}@@:選項內容有@@color:green;{{{<<option>>}}}@@和@@color:green;{{{<<optionsfrom>>}}}@@兩種寫法 * @@color:green;{{{<<option label [value [selected]]>>}}}@@: > 1. @@color:green;{{{label}}}@@:顯示於列表的選項名稱 > 2. @@color:green;{{{value}}}@@:選填,選擇此選項儲存到變數的值 > 3. @@color:green;{{{selected}}}@@:選填,用來標記為顯示於輸入框的預設選項。autoselect和selected不能同時使用。 > 4. 語法範例: >><div style="color:green;"> {{{ <<listbox "$lbanswer">> <<option "π" 3.14159>> <<option 42>> <<option "∞" 無限>> <<option "預設選項" selected>> <</listbox>> }}} </div> * @@color:green;{{{<<optionsfrom collection>>}}}@@: > 1. @@color:green;{{{collection}}}@@:集合(collection) 類型的資料結構,如陣列(Array)、集合(Set)、物件(Objects)、Map >><table style="border:1px #cccccc solid;" cellpadding="10" border="1"> <tr><td><strong>集合(collection)類型</strong></td><td style="text-align:center;"><strong>選填:label, value</strong></td></tr> <tr><td>陣列(Array)、集合(Set)</td><td style="text-align:center;">Member: value, value</td></tr> <tr><td>Generic objects</td><td style="text-align:center;">Property: name, value</td></tr> <tr><td>Maps</td><td style="text-align:center;">Eentry: key, value</td></tr> </table> > 2. 語法範例: >><div style="color:green;"> {{{ <<set _options = ["物品1", "物品2", "物品3"]>> <<listbox "$lbanswer" autoselect>> <<optionsfrom _options>> <</listbox>> }}} </div> ---- 【實例】 <<listbox "$lbanswer">> <<option "π" 3.14159>> <<option 42>> <<option "∞" 無限>> <<option "預設選項" selected>> <</listbox>> 商店中有:<<listbox "$listget" autoselect>><<optionsfrom $listoption>><</listbox>><<button '購買'>><<goto '下拉式選單物品'>><</button>> <<return 返回>>
在特定處跳出提醒通知,通知內容會自動顯示與消失。 !!!!<<message '1.在「{ } Javascript」置入Javascript程式碼'>> @@color:green;<<include 'notifyjs'>>@@ <</message>> !!!!<<message '2.在「# Stylesheet」置入CSS語法'>> @@color:green;<<include 'notifycss'>>@@ <</message>> !!!!<<message '3.在跳出提醒通知的Passage置入語法'>> @@color:green;{{{<<notify>>}}}@@通知內容@@color:green;{{{<</notify>>}}}@@ 點擊特定按鈕跳出通知的語法: @@color:green;{{{<<button"}}}@@跳出通知@@color:green;{{{">><<notify>>}}}@@通知內容@@color:green;{{{<</notify>>}}}@@@@color:green;{{{<</button>>}}}@@ <</message>> ---- 【實例】 <<button"跳出通知">><<notify>>這是通知!!!!!!<</notify>><</button>> <<return 返回>>
自動存檔功能透過Save API來達成。 在「{ } Javascript」置入以下代碼,就能在每個Passage自動存檔(會默認儲存為編號A,顯示於存檔第一列) @@color:green;{{{ Config.saves.autosave = true; }}}@@ 函式回傳真值時自動存檔@@color:green; {{{ Config.saves.autosave = function () { /* code */ }; }}} @@ 如果只要在特定的Passage自動存檔,透過標籤(Tag)來達成。 # 在「{ } Javascript」置入以下代碼<br>@@color:green;{{{Config.saves.autosave = [“bookmark”, “autosave”];}}}@@ # 在自動存檔的Passage增加標籤「autosave」或「bookmark」 > <img src="https://64.media.tumblr.com/a768f5ec4e8178093c176220442a8fad/fc5d571953fb97fe-29/s1280x1920/a7a9b7125bd6f997e886e345ff3745991281bba2.png" width="500px" > 如果想要在自動存檔時跳出通知,可搭配[[通知提醒]]功能來實現。但如果要在每個有「autosave」標籤的Passage中增加語法,會太過麻煩,可以在PassageHeader中加入以下語法(PassageHeader的內容會在每個Passage頂部區塊運行、呈現)。 @@color:green;{{{ <<if tags().includes(‘autosave’)>><<notify 5s>>已自動儲存!<</notify>><</if>> }}}@@ ---- 【實例】 你已經自動存檔,,<<link '開啟存檔'>><<script>>UI.saves()<</script>><</link>> [[繼續|自動存檔1]] <<return 返回>>
<<if Save.autosave.ok() and Save.autosave.has()>>\ 有自動存檔紀錄,是否繼續遊戲或從頭開始? <<button "載入遊戲">> <<script>>Save.autosave.load()<</script>> <</button>> \ <<button "重新開始" "start">> <<script>>Save.autosave.delete()<</script>> <</button>> <<else>> <<goto "自動存檔2">> <</if>>
沒有存檔紀錄 <<return 返回>>
我選擇的是:$listget <<return 返回>>
<div id='vertical-writing'>文字直書</div> * 新增CSS程式碼 @@color:grey; {{{ /* 文字直書 */ #vertical-writing { -webkit-writing-mode: vertical-rl; writing-mode: vertical-rl; } }}} @@ * 加上語法 @@color:green;{{{<div id='vertical-writing'>}}}@@文字直書@@color:green;{{{</div>}}}@@
如果在Passage中置入的Javascript程式碼要運作在特定的HTML標記上,可以使用@@ev.content@@對應運作的HTML標記: @@color:green; {{{ <<script>> $(document).one(":Passagerender", function (ev) { // 包含"has-map"標籤的Passage才會生效 if(tags().includes("has-map")) { // 程式碼加在這邊,使用"ev.content"對應運作的HTML標記。 // 比如說,要使用JQuery的程式碼可以這麼寫: $(ev.content).find("#CopyItm button").prop("disabled", true); } }); <</script>> }}} @@ *[[Javascript導航事件|http://www.motoslave.net/sugarcube/2/docs/#events-navigation]] ---- 在「{ } Javascript」中使用的JavaScript程式碼為全域通用,當遊戲開始或重新載入時,在「{ } Javascript」都會運行,因此建議利用SugarCube的特殊Setup對象或特殊Window對象上定義函數,讓遊戲在遇到特定事件時才觸發Javascript程式碼。 * 在「{ } Javascript」中使用的JavaScript程式碼: @@color:green; {{{ /* 下面程式碼會在遊戲啟動後立即生效 */ var timeout = setTimeout(triggeredCode, 2000); // 2秒後調用"triggeredCode"函數 /* 下面程式碼只有在調用Window對象函數時才會生效 */ // 可全域使用,也能從 HTML 元素標籤的 on<event-name>屬性存取它 window.triggeredCode = function() { alert("This is JavaScript."); }; /* 下面程式碼只有在調用Setup對象函數時才會生效 */ setup.hello = function (name) { return "Hello ' + name + ', how are you?'; }; }}} @@ * 在Passage使用以下程式碼調用Window對象函數: @@color:green; {{{ <<= triggeredCode()>> <<= window.triggeredCode()>> <<button "Click me">><<run triggeredCode()>><</button>> }}} @@ * 在Passage使用以下程式碼調用Setup對象函數: @@color:green; {{{ <<= setup.hello('Jane')>> <<button "Click me">><<run setup.hello('Jane')>><</button>> }}}
!!!置入影片 !!!!twine語法: @@color:green;{{{<video src="}}}@@影片連結@@color:green;{{{" width="}}}@@寬(px)@@color:green;{{{" height="}}}@@高(px)@@color:green;{{{" > </video>}}}@@ 寬、高擇一放就好,亦可用%,會依照頁面比例縮放。 !!!!Youtube嵌入語法: 1.點撃分享中的嵌入選項 <img src="media/img/youtubeiframe.png" width="400px"> 2.點撃右下角的複製,再將複製内容直接貼到Passage裡就行 語法呈現結果 <iframe width="560" height="315" src="https://www.youtube.com/embed/OOBmfyUgN7k" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> <<return 返回>>
在CSS語法中,透過//CSS選擇器(CSS Selectors)//來選定要調整哪個/哪些元素的樣式。 @@color:green; {{{ Selector { Property: Value; } }}} @@↑ Selector:選擇器、Property:屬性、Value:屬性值 常用的選擇器有三種:@@類別(元素)@@、@@id@@、@@class@@。 <<message '1.類別(元素)'>> 直接將指定的元素標籤名稱當做選擇器,例如: @@color:green;{{{ h1 { color: #333333; } }}}@@ ↑上面範例的樣式將會套用到頁面所有的@@color:green;{{{<h1>~</h1>}}}@@ <</message>> <<message '2.id'>> 利用 id 屬性為元素命名並做為選擇器,元素(標籤)名稱通常省略不寫。 @@color:red;要注意id是用來辨識特定位置的屬性,若同一頁面中撰寫多次相同id名稱,會造成語法錯誤。@@ 例如: @@color:green;{{{ #title { color: #333333; } }}}@@ ↑像@@color:green;{{{<div id='title'>~</div>}}}@@、@@color:green;{{{<span id='title'>~</span>}}}@@等id為title的元素,將會受上面範例的樣式影響。 <</message>> <<message '3.class'>> 利用 class 屬性為元素命名並做為選擇器,當元素(標籤)名稱省略不寫時,會被視為//通用的class//,任何元素標籤都可以使用。 @@color:red;要注意class和id不同,可以在HTML檔中多處重複使用。@@ 例如: @@color:green;{{{ .text-green { color: #32b16c; } }}}@@ ↑像@@color:green;{{{<div class='text-green'>~</div>}}}@@、@@color:green;{{{<span class='text-green'>~</span>}}}@@等class為text-green的元素,將會受上面範例的樣式影響。 <</message>> 想更進一步了解選擇器的用法,可以參考[[此文章說明|https://developer.mozilla.org/zh-TW/docs/Glossary/CSS_Selector]] <<return 返回>>
視覺小說常見的對話功能,在同一個Passage中,透過"上一句"、"下一句"連結瀏覽對話內容。 !!!!<<message '1.在「{ } Javascript」置入Javascript程式碼'>> @@color:green;<<include 'speechboxjs'>>@@ <</message>> !!!!<<message '2.在「# Stylesheet」置入CSS語法'>> @@color:green;<<include 'speechboxcss'>>@@ <</message>> !!!!<<message '3.新增Widgets的Passage'>> 建立一個叫"Widgets"的Passage,點選「+Tag」新增標籤widget和標籤nobr。 <img src="media/img/speechboxtag.jpg" width="250px" > 建立完成後,將以下語法置入"Widgets": @@color:green;<<include 'speechboxwidget'>>@@ <</message>> !!!!<<message '4.在使用對話系統的Passage置入語法'>> @@color:green; {{{ <<speechBOX 下一Passage名稱 角色名稱 角色圖片位置>> <<ctp "test">><<set $back_dialog = "test">><<set _ctp to CTP.getCTP("test")>> 對話內容 <</ctp>> <</speechBOX>> }}} @@@@color:green;對話內容@@:置入角色對話內容,並依據出現的情況來撰寫語法。以@@color:green;{{{<<ctpNext 關鍵字>>}}}@@來置入下一句對話,關鍵字依出現狀況來替換成其他文字: # clear:清除上一個對話的內容。 # nobr:將新對話內容附加於上一句對話之後,之間不空新行。 # t8n:新對話內容出現時加入過渡動畫。 # (delay):置入CSS時間值,如3s或250ms,來指定新對話內容出現的延遲時間。 例如: > <div>@@color:green; {{{ 第一句話。 <<ctpNext clear>> 第二句話,會蓋掉第一句話。 <<ctpNext>> 第三句話,接在第二句話下一行。 <<ctpNext nobr>> 第四句話,接在第三句後面。 <<ctpNext t8n>> 第五句話,接在第四句下一行且淡出出現。 <<ctpNext 0.5s>> 第六句話,0.5秒後才出現。 }}} @@</div> <<message '完整語法範例'>> 假設對話結束後進入到「故事結束」的Passage,對話角色名稱為「記者」,角色圖片位置為「media/img/player.png」。 @@color:green; {{{ <<speechBOX 故事結束 記者 media/img/player.png>> <<ctp "test">><<set $back_dialog = "test">><<set _ctp to CTP.getCTP("test")>> 第一句話。 <<ctpNext clear>> 第二句話,會蓋掉第一句話。 <<ctpNext>> 第三句話,接在第二句話下一行。 <<ctpNext nobr>> 第四句話,接在第三句後面。 <<ctpNext t8n>> 第五句話,接在第四句下一行且淡出出現。 <<ctpNext 0.5s>> 第六句話,0.5秒後才出現。 <</ctp>> <</speechBOX>> }}} @@<</message>><</message>> ---- 【實例】 [[版面範例|https://3zhiyu.github.io/tinatest/%E5%B0%8D%E8%A9%B1%E7%B3%BB%E7%B5%B1.html]] <<return 返回>>
<strong>1.在twine中置入CSS語法</strong> 在工作列的「Story」中選取「# Stylesheet」,然後把程式碼複製貼上即可。 <img src="media/img/stylesheetputin.png" width="400px" > <strong>2.於行內樣式使用CSS語法</strong> 在 HTML tag裡,透過style=" "新增CSS語法 @@color:green;{{{<div style="color:green">~</div>}}}@@ <strong>3.匯入外部CSS語法</strong> 如果要以urls方式導入外部CSS,則在「# Stylesheet」中置入以下程式碼: @@color:green;{{{importStyles("https://somesite/a/path/a.css", "https://somesite/a/path/b.css")}}}@@ <<return 返回>>
<strong>1.在twine中置入Javascript程式碼</strong> 在工作列的「Story」中選取「{ } Javascript」,然後把程式碼複製貼上即可。 <img src="media/img/javascriptputin.png" width="400px" > <strong>2.於Passage中使用Javascript</strong> 需利用{{{<<script>>}}}來使用JavaScript,在Passage裡加入下面的程式碼: @@color:green; {{{ <<script>> // 這邊置入Javascript程式碼內容,以下是彈出警告視窗的程式碼範例 alert("This is JavaScript.");}}} <</script>> }}} @@ <strong>3.匯入外部Javascript程式碼</strong> 如果要以urls方式導入外部Javascript程式碼,則在「{ } Javascript」中置入以下程式碼: @@color:green;{{{importScripts("https://somesite/a/path/a.js", "https://somesite/a/path/b.js")}}}@@ <<return 返回>>
<<if $key>> 門打開了。 <<else>> 無法開門。 <</if>> <<return 返回>>
* 新增CSS程式碼 @@color:green; {{{ @-webkit-keyframes shakeanim { 0% { -webkit-transform: translate(2px, 1px) rotate(0deg); } 10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); } 20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); } 30% { -webkit-transform: translate(0px, 2px) rotate(0deg); } 40% { -webkit-transform: translate(1px, -1px) rotate(1deg); } 50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); } 60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); } 70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); } 80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); } 90% { -webkit-transform: translate(2px, 2px) rotate(0deg); } 100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); } } @-o-keyframes shakeanim { 0% { -o-transform: translate(2px, 1px) rotate(0deg); } 10% { -o-transform: translate(-1px, -2px) rotate(-1deg); } 20% { -o-transform: translate(-3px, 0px) rotate(1deg); } 30% { -o-transform: translate(0px, 2px) rotate(0deg); } 40% { -o-transform: translate(1px, -1px) rotate(1deg); } 50% { -o-transform: translate(-1px, 2px) rotate(-1deg); } 60% { -o-transform: translate(-3px, 1px) rotate(0deg); } 70% { -o-transform: translate(2px, 1px) rotate(-1deg); } 80% { -o-transform: translate(-1px, -1px) rotate(1deg); } 90% { -o-transform: translate(2px, 2px) rotate(0deg); } 100% { -o-transform: translate(1px, -2px) rotate(-1deg); } } @keyframes shakeanim { 0% { transform: translate(2px, 1px) rotate(0deg); } 10% { transform: translate(-1px, -2px) rotate(-1deg); } 20% { transform: translate(-3px, 0px) rotate(1deg); } 30% { transform: translate(0px, 2px) rotate(0deg); } 40% { transform: translate(1px, -1px) rotate(1deg); } 50% { transform: translate(-1px, 2px) rotate(-1deg); } 60% { transform: translate(-3px, 1px) rotate(0deg); } 70% { transform: translate(2px, 1px) rotate(-1deg); } 80% { transform: translate(-1px, -1px) rotate(1deg); } 90% { transform: translate(2px, 2px) rotate(0deg); } 100% { transform: translate(1px, -2px) rotate(-1deg); } } .shake-block, .shake { -webkit-animation-duration: 0.8s; -o-animation-duration: 0.8s; animation-duration: 0.8s; -webkit-animation-iteration-count: infinite; -o-animation-iteration-count: infinite; animation-iteration-count: infinite; -webkit-animation-name: shakeanim; -o-animation-name: shakeanim; animation-name: shakeanim; -webkit-animation-timing-function: linear; -o-animation-timing-function: linear; animation-timing-function: linear; -webkit-transform-origin: 50% 50%; -ms-transform-origin: 50% 50%; -o-transform-origin: 50% 50%; transform-origin: 50% 50%; } .shake-block { display: block; } .shake { display: inline-block; } }}} @@ * 新增Javascript程式碼 @@color:green; <p>{{{ /* 動態效果:<<shake>> */ !function(){"use strict";if("undefined"==typeof version||"undefined"==typeof version.title||"SugarCube"!==version.title||"undefined"==typeof version.major||version.major<2||"undefined"==typeof version.minor||version.minor<5)throw new Error("<<shake>> macro requires SugarCube 2.5.0 or greater, aborting load");Macro.add("shake",{tags:null,handler:function(){var duration=this.args.length>0?this.args[0]:1/0,shakeClass="shake";if(1/0!==duration)try{duration=Math.max(Engine.minDomActionDelay,Util.fromCssTime(duration))}catch(e){return this.error(e.message)}Config.debug&&this.debugView.modes({block:!0});var $wrapper=jQuery(document.createElement("span"));$wrapper.addClass("macro-"+this.name+" "+shakeClass).wiki(this.payload[0].contents).appendTo(this.output),1/0!==duration&&setTimeout(function(){$wrapper.removeClass(shakeClass)},Engine.minDomActionDelay+duration)}}),Macro.add(["shakescreen","shaketarget"],{handler:function(){var $targets,duration,shakeClass;if("shakescreen"===this.name)$targets=jQuery("#passages"),duration=this.args.length>0?this.args[0]:1/0,shakeClass="shake-block";else{if(0===this.args.length)return this.error("no selector specified");if($targets=jQuery(this.args[0]),0===$targets.length)return this.error('no elements matched the selector "'+this.args[0]+'"');duration=this.args.length>1?this.args[1]:1/0,shakeClass="block"===jQuery($targets[0]).css("display")?"shake-block":"shake"}if("stop"===duration)return void $targets.removeClass(shakeClass);if(1/0!==duration)try{duration=Math.max(Engine.minDomActionDelay,Util.fromCssTime(duration))}catch(e){return this.error(e.message)}$targets.addClass(shakeClass),1/0!==duration&&setTimeout(function(){$targets.removeClass(shakeClass)},Engine.minDomActionDelay+duration)}})}(); }}}</p> @@ * 加上語法 @@color:green;{{{<<shake>>}}}@@搖擺效果@@color:green;{{{<</shake>>}}}@@ <<shake>>搖擺效果<</shake>>
<!-- 視覺小說版面的角色對話框 --> <<widget "speechBOX" container>> <<set _linklock to false>>\ <div id="speechBOX">\ <span style='background-color:red;'><<print _args[0]>></span> ---- _contents <table class='speechtable'> <tr> <td class='left'><<liveblock>><span id='back'><<if _ctp.log.index gt 0>><<if _linklock == false>><<scicon step-backward>><<link "上一句">><<ctpBack "對話">><<update>><</link>><</if>><</if>></span><</liveblock>></td> <td class='right'><<liveblock>><span id='next'><<if _ctp.log.index lt _ctp.stack.length - 1>><<if _linklock == false>><<link "下一句">><<ctpAdvance "對話">><<update>><</link>><<scicon step-forward>><</if>><</if>></span><</liveblock>></td> </tr> </table></div> <</widget>> <!-- sugarcube ICON --> <<widget "scicon">><span @class="'sc-icon sc-' + $args.raw" alt="' + $args.raw + '"></span><</widget>>
如果你想在一張圖上設定有多個可以點擊互動的部位,那麼你就需要靠影像地圖系統來達到功能。 !!!!<<message '1.將圖片建立成影像地圖(在圖檔上劃分出區塊當成超連結使用)'>> * 使用[[此網站|https://www.image-map.net/]]建立影像地圖 **上傳圖檔後,於下方區塊設定列中的Shape選擇類型為Rect,在於圖上畫出要互動的區塊範圍 **如果要設定多個互動區塊,點擊區塊設定列中的「Add New Area」按鈕來新增區塊 **建立完區塊後,點擊「Show Me The Code!」按鈕,然後複製@@color:green;{{{<map name="image-map">}}}@@~@@color:green;{{{</map>}}}@@這部分~的語法內容 **@@color:red;注意:@@複製的語法{{{<area>}}}結尾的>前要加/,並把@@color:green;href@@改成@@color:green;onclick@@ **{{{<area target="" alt="" title="" onclick="" coords="806,284,888,311" shape="rect"}}} @@color:red;/@@{{{>}}} <</message>> !!!!<<message '2.回到twine,在要置入影像地圖的passage複製貼上以下語法'>> @@color:green; {{{ <<nobr>> <map name="infographic"> (貼上剛在網站上複製的語法) </map> <div class="resizable imageMapObserve" style="width: 200px;"> <img usemap="#infographic" alt="影像地圖" src="圖片位址" /> </div> <</nobr>> }}} @@ <</message>> !!!!<<message '3.編輯可互動區域的語法,設定互動方式'>> * 編輯從網站上複製的語法,加入互動語法 * @@color:green;{{{<area target="" alt="" title=""}}}@@ @@color:orange;{{{onclick="互動語法"}}}@@ @@color:green;{{{href="" coords="569,361,690,444" shape="rect" />}}}@@ ** 比如加入{{{<<goto>>}}}來鏈接到另一段落的互動功能,互動語法為:@@color:orange;{{{$.wiki('<<goto "Passage名稱">>')}}}@@ ** 要注意雙引號要改成@@color:green;{{{"}}}@@,單引號要改成@@color:green;{{{'}}}@@ <</message>> !!!!<<message '4.加入CSS語法設定影像地圖樣式'>> 在「# Stylesheet」置入以下語法: @@color:green;<<include 'imagemapcss'>>@@ <</message>> !!!!<<message '5.加入Javascript語法'>> 在「{ } Javascript」置入以下程式碼: @@color:green;<<include 'imagemapjs'>>@@ <</message>> [[語法來源|https://qjzhvmqlzvoo5lqnrvuhmg.on.drv.tw/UInv/Sample_Code.html#Clicking%20Parts%20of%20Images]] ---- 【實例】 [[範例|https://3zhiyu.github.io/tinatest/%E5%BD%B1%E5%83%8F%E5%9C%B0%E5%9C%96%E7%B3%BB%E7%B5%B1.html]] <<return 返回>>
Sugarscube內建巨集: @@color:green;{{{<<include "Passage名稱">>}}}@@或@@color:green;{{{<<include [[Passage名稱]]>>}}}@@ 輸出指定Passage的內容,假設我要置入"A片段"這個Passage的內容,語法為@@color:green;{{{<<include "A片段">>}}}@@ 如果要把輸出的內容包裹在{{{<div>}}}裡,語法寫法為: @@color:green;{{{<<include "A片段" "div">>}}}@@ ---- 【實例】 A片段的內容:<<include "A片段">> A片段的內容(包裹在{{{<div>}}}裡):<<include "A片段" "div">> <<return 返回>>
透過Config API來實現,在「{ } Javascript」新增程式碼(影響全遊戲)或用@@color:green;{{{<<script>>}}}@@加入程式碼。 1. 禁用側邊欄←、→的按鈕 @@color:green;{{{ // 禁用側邊欄←、→ Config.history.controls = false; }}}@@ 2. 收合打開側邊欄(在「{ } Javascript」使用會讓遊戲一開始就收合側邊欄) @@color:green;{{{ // 收合側邊欄 UIBar.stow(); // 收合側邊欄但不要動畫 UIBar.stow(true); // 打開側邊欄 UIBar.unstow(); // 隱藏側邊欄 UIBar.hide(); // 刪除側邊欄 UIBar.destroy(); }}} @@ <<return 返回>>
在twine遊戲中,如果要在特定時刻彈出小視窗,可透過Dialog API來實現。 1.彈出視窗 @@color:green; {{{ <<run Dialog.setup("視窗標題")>> <<run Dialog.wiki("視窗內容。")>> <<run Dialog.open()>> }}} @@ 或是用Javascript(怎麼在Passage中使用Javascript請見[[此處|Javascript使用說明]]說明) @@color:green; {{{ <<script>> Dialog.setup("視窗標題"); Dialog.wiki("視窗內容。"); Dialog.open(); <</script>> }}} @@ ↑這邊的@@color:green;"視窗內容。"@@也可以輸入巨集語法,比如改成(""記得要保留): @@color:green;{{{你知道下一個地點了!<br><<button '前往下一個地點'>><<run Dialog.close()>><</button>>}}}@@ * 如果視窗的內容要輸入指定Passage內容,把@@color:green;{{{<<run Dialog.wiki("視窗內容。")>>}}}@@這段語法改寫如下: @@color:green;{{{<<run Dialog.wiki(Story.get('}}}@@@@color:red;Passage名稱@@@@color:green;{{{').processText())>>}}}@@ * 如果要點擊連結或按鈕開啟視窗,在@@color:green;{{{<<link>>~<</link>>}}}@@或@@color:green;{{{<<button>>~<</button>>}}}@@之間置入Dialog API,完整語法如下:@@color:green; {{{ /* 下面是連結的語法 */ <<link '連結名稱'>> <<run Dialog.setup("視窗標題")>> <<run Dialog.wiki("視窗內容。")>> <<run Dialog.open()>> <</link>> /* 下面是按鈕的語法 */ <<button '按鈕名稱'>> <<run Dialog.setup("視窗標題")>> <<run Dialog.wiki("視窗內容。")>> <<run Dialog.open()>> <</button>> }}} @@ 2.關閉視窗 @@color:green;{{{<<run Dialog.close()>>}}}@@ 或 @@color:green;{{{<<script>>Dialog.close();<</script>>}}}@@ * 如果要點擊連結或按鈕開啟視窗,在@@color:green;{{{<<link>>~<</link>>}}}@@或@@color:green;{{{<<button>>~<</button>>}}}@@之間置入Dialog API,完整語法如下:@@color:green; {{{ /* 下面是連結的語法 */ <<link '連結名稱'>> <<run Dialog.close()>> <</link>> /* 下面是按鈕的語法 */ <<button '按鈕名稱'>> <<run Dialog.close()>> <</button>> }}} @@ ---- 【實例】 <<link '開啟視窗'>> <<run Dialog.setup("視窗標題")>> <<run Dialog.wiki('視窗內容。<br><<link "關閉視窗">><<run Dialog.close()>><</link>>')>> <<run Dialog.open()>> <</link>><<button "彈出視窗">> <<run Dialog.setup("視窗標題")>> <<run Dialog.wiki('視窗內容。<br><<button "關閉視窗">><<run Dialog.close()>><</button>>')>> <<run Dialog.open()>> <</button>> <<return 返回>>
我是A片段的內容
@@color:green;{{{@@.center;}}}@@文字置中@@color:green;{{{@@}}}@@ @@.center;文字置中@@ @@color:green;{{{@@.delayed;}}}@@第一行文字@@color:green;{{{@@}}}@@ @@color:green;{{{@@.delayed;}}}@@第二行文字@@color:green;{{{@@}}}@@ @@color:green;{{{@@.delayed;}}}@@第三行文字@@color:green;{{{@@}}}@@ 文字逐行出現 @@color:green;{{{<<timed 1s t8n>>}}}@@第一行文字 @@color:green;{{{<<next>>}}}@@第二行文字 @@color:green;{{{<<next>>}}}@@第三行文字 @@color:green;{{{<</timed>>}}}@@ 固定@@color:red;一秒@@後,逐行出現文字 @@color:green;{{{<span class="glitch">}}}@@發抖效果@@color:green;{{{</span>}}}@@ <span class="glitchy" data-text="抖動效果">發抖效果</span> 單一文字@@color:green;{{{<span class="glitchy" data-text="}}}@@恐怖@@color:green;{{{">}}}@@發抖@@color:green;{{{</span>}}}@@ 單一文字<span class="glitchy" data-text="恐怖">發抖</span> @@color:green;{{{<span class="drunk">}}}@@喝醉效果@@color:green;{{{</span>}}}@@ @@color:green;{{{@@.drunk;}}}@@喝醉效果@@color:green;{{{@@}}}@@ <span class="drunk">喝醉效果</span> @@color:green;{{{@@.shadow;}}}@@文字陰影效果@@color:green;{{{@@}}}@@ @@.shadow;文字陰影效果@@ @@color:green;{{{@@.emboss;}}}@@浮凸字體效果@@color:green;{{{@@}}}@@ @@.emboss;浮凸字體效果@@ @@color:green;{{{@@.blur;}}}@@模糊字體效果@@color:green;{{{@@}}}@@ @@.blur;模糊字體效果@@ @@color:green;{{{@@.blurrier;}}}@@焦糊字體效果@@color:green;{{{@@}}}@@ @@.blurrier;焦糊字體效果@@ @@color:green;{{{@@.smear;}}}@@光暈字體效果@@color:green;{{{@@}}}@@ @@.smear;光暈字體效果@@ @@color:green;{{{@@.mirror;}}}@@字體鏡射效果@@color:green;{{{@@}}}@@ @@.mirror;字體鏡射效果@@ @@color:green;{{{@@.upside-down;}}}@@字體上下顛倒效果@@color:green;{{{@@}}}@@ @@.upside-down;字體上下顛倒效果@@ @@color:green;{{{@@.fade-in-out;}}}@@字體閃爍效果@@color:green;{{{@@}}}@@ @@.fade-in-out;字體閃爍效果@@ @@color:green;{{{@@.rumble;}}}@@字體上下抖動效果@@color:green;{{{@@}}}@@ @@.rumble;字體上下抖動效果@@ @@color:green;{{{@@.shudder;}}}@@字體左右抖動效果@@color:green;{{{@@}}}@@ @@.shudder;字體左右抖動效果@@ @@color:green;{{{@@.pulse;}}}@@字體彈出效果@@color:green;{{{@@}}}@@ @@.pulse;字體彈出效果@@ <<return 返回>>
{{{ <!-- 對話系統 --> /* 自訂巨集<<ctp>>(Cycy wrote custom macros) */ (function () { "use strict"; window.CTP = function (config) { this.id = ""; this.selector = ""; this.stack = []; this.head = ""; this.tail = ""; this.log = { clear: 0, index: 0, seen: 0, delayed: false }; Object.keys(config).forEach(function (pn) { this[pn] = clone(config[pn]); }, this); }; CTP.prototype.clone = function () { return new CTP(this); }; CTP.prototype.toJSON = function () { var ownData = {}; Object.keys(this).forEach(function (pn) { ownData[pn] = clone(this[pn]); }, this); return JSON.reviveWrapper('new CTP($ReviveData$)', ownData); }; CTP.getCTP = function (id) { var clone = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!id || !id.trim()) throw new Error("No ID specified!"); variables()["#macro-ctp-dump"] = variables()["#macro-ctp-dump"] || {}; if (!variables()["#macro-ctp-dump"][id]) throw new Error("No CTP with ID '" + id + "' found!"); return clone ? variables()["#macro-ctp-dump"][id].clone() : variables()["#macro-ctp-dump"][id]; }; CTP.contentObject = function (content) { var mods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ""; mods = mods.split(/\s+/g); return { clear: mods.includes("clear") && !mods.includes("noClear"), nobr: mods.includes("nobr") && !mods.includes("br"), transition: mods.includesAny("t8n", "transition") && !mods.includesAny("noT8n", "noTransition"), delay: Util.fromCssTime(mods.find(function (el) { return /[0-9\.]+m?s/.test(el); }) || "0s"), re: mods.includes("redo"), content: content }; }; CTP.prototype.add = function (content) { var mods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ""; var contentObj = CTP.contentObject(content, mods); contentObj.index = this.stack.length; this.stack.push(contentObj); return this; }; CTP.item = function (item) { var noT8n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!item) return ""; var t8n = noT8n ? "" : item.transition ? "macro-ctp-entry-t8n" : ""; var br = item.index === 0 || item.index === "head" || item.clear ? " " : item.nobr ? " " : '<br class="macro-ctp-entry-index-' + item.index + '">'; var brAfter = item.index === "head" && !item.nobr ? "<br>" : " "; return br + '<span class="macro-ctp-visible macro-ctp-entry macro-ctp-entry-index-' + item.index + ' ' + t8n + '">' + item.content + '\n</span>' + brAfter; }; CTP.prototype.entry = function (index) { var noT8n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (index < 0 || index >= this.stack.length) return ""; var entry = this.stack[index]; return CTP.item(entry, noT8n); }; CTP.prototype.out = function () { return '<span class="macro-ctp-wrapper">' + '<span class="ctp-head"></span>' + '<span class="ctp-body">' + this.entry(0) + '</span>' + '<span class="ctp-tail"></span>' + '</span>'; }; CTP.prototype.go = function (diff) { if (!Number.isInteger(diff)) throw new Error(`Cannot move by non-integral amounts!`); if (diff > 0) { for (var i = 0; i < diff; i++) { this.advance(true); } } else if (diff < 0) { for (var _i = 0; _i < -diff; _i++) { this.back(); } } }; CTP.prototype.goTo = function (index) { this.go(index - this.log.index); }; CTP.prototype.advance = function () { var noDelay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var _this = this; if (this.log.index === this.stack.length - 1 || this.log.delayed) return; var index = ++this.log.index; this.log.seen = Math.max(this.log.seen, index); var _el = $(this.selector).children(".ctp-body"); if (this.stack[index].clear) { _el.children().removeClass("macro-ctp-visible").hide(); this.log.clear = index - 1; } function delay(ctp) { ctp.log.delayed = false; $(ctp.selector).children(".ctp-body").wiki(ctp.entry(ctp.log.index)).siblings(".ctp-head").empty().wiki(CTP.item(ctp.head)).siblings(".ctp-tail").empty().wiki(CTP.item(ctp.tail)); } if (noDelay) { delay(_this); } else { this.log.delayed = true; return new Promise(function (resolve, reject) { setTimeout(function () { delay(_this); resolve("advanced"); }, _this.stack[index].delay); }); } }; CTP.prototype.back = function () { var _this3 = this; if (this.log.index <= 0 || this.log.delayed) return this; if (this.log.clear >= --this.log.index) { var clearIndex = 0; var _clear = this.stack.filter(function (el) { return el.clear && el.index < _this3.log.index + 1; }); if (_clear.length) clearIndex = _clear[_clear.length - 1].index; this.stack.slice(clearIndex, this.log.index + 1).forEach(function (el) { $(_this3.selector).children(".ctp-body").children(".macro-ctp-entry-index-" + el.index).addClass("macro-ctp-visible").show(); }, this); } var item = this.stack[this.log.index]; if (item.re) $(this.selector).children(".ctp-body").children(".macro-ctp-entry.macro-ctp-entry-index-" + item.index).empty().wiki(item.content); this.stack.slice(this.log.index + 1, this.log.seen + 1).forEach(function (el) { $(_this3.selector).children(".ctp-body").children(".macro-ctp-entry-index-" + el.index).remove(); }, this); $(this.selector).children(".ctp-head").empty().wiki(CTP.item(this.head)).siblings(".ctp-tail").empty().wiki(CTP.item(this.tail)); return this; }; Macro.add("ctp", { tags: ["ctpNext", "ctpHead", "ctpTail"], handler: function handler() { if (this.args.length === 0) return this.error("No ID specified!"); var _id = this.args[0]; var _data = 'data-ctp="' + Util.escape(_id) + '"'; var ctp = new CTP({ id: _id, selector: '[' + _data + ']' }); var _overArgs = this.payload[0].args; _overArgs.reverse().pop(); _overArgs = " " + _overArgs.join(" "); this.payload.forEach(function (el, index) { var _args = el.args.join(" "); switch (el.name) { case "ctpHead": { var _head = CTP.contentObject(el.contents.trim(), _args + _overArgs); _head.index = "head"; ctp.head = _head; break; } case "ctpTail": { var _tail = CTP.contentObject(el.contents.trim(), _args + _overArgs); _tail.index = "tail"; ctp.tail = _tail; break; } default: { ctp.add(el.contents.trim(), (el.name === "ctp" ? "" : _args) + _overArgs); break; } } }); variables()["#macro-ctp-dump"] = variables()["#macro-ctp-dump"] || {}; variables()["#macro-ctp-dump"][_id] = ctp; let out = $("<div />"); out.wiki(ctp.out()).children(".macro-ctp-wrapper").attr("data-ctp", Util.escape(_id)).children(".ctp-head").wiki(CTP.item(ctp.head)).siblings(".ctp-tail").wiki(CTP.item(ctp.tail)); $(this.output).append(out.children().unwrap()); } }); Macro.add("ctpAdvance", { handler: function handler() { try { var ctp = CTP.getCTP(this.args[0]); if (ctp) ctp.advance(); } catch (ex) { throw new Error(_typeof(ex) === 'object' ? ex.message : ex); } } }); Macro.add("ctpBack", { handler: function handler() { try { var ctp = CTP.getCTP(this.args[0]); if (ctp) ctp.back(); } catch (ex) { throw new Error(_typeof(ex) === 'object' ? ex.message : ex); } } }); $(document).on(':passageinit', function () { delete variables()["#macro-ctp-dump"]; }); })(); /* 即時更新:Live Update */ (function () { "use strict"; $(document).on(":liveupdate", function () { $(".macro-live").trigger(":liveupdateinternal"); }); Macro.add(['update', 'upd'], { handler: function handler() { $(document).trigger(":liveupdate"); } }); Macro.add(['live', 'l', 'lh'], { skipArgs: true, handler: function handler() { if (this.args.full.length === 0) { return this.error('no expression specified'); } try { var statement = this.args.full; var result = toStringOrDefault(Scripting.evalJavaScript(statement), null); if (result !== null) { var lh = this.name === "lh"; var $el = $("<span></span>").addClass("macro-live").wiki(lh ? Util.escape(result) : result).appendTo(this.output); $el.on(":liveupdateinternal", this.createShadowWrapper(function (ev) { var out = toStringOrDefault(Scripting.evalJavaScript(statement), null); $el.empty().wiki(lh ? Util.escape(out) : out); })); } } catch (ex) { return this.error("bad evaluation: " + (_typeof(ex) === 'object' ? ex.message : ex)); } } }); Macro.add(['liveblock', 'lb'], { tags: null, handler: function handler() { try { var content = this.payload[0].contents.trim(); if (content) { var $el = $("<span></span>").addClass("macro-live macro-live-block").wiki(content).appendTo(this.output); $el.on(":liveupdateinternal", this.createShadowWrapper(function (ev) { $el.empty().wiki(content); })); } } catch (ex) { return this.error("bad evaluation: " + (_typeof(ex) === 'object' ? ex.message : ex)); } } }); })(); }}}
{{{ /* 對話系統:自訂巨集<<ctp>>(Cycy wrote custom macros) */ .macro-ctp-entry { display: inline-block; } .macro-ctp-entry-t8n { animation: macro-ctp-fade-in 0.4s ease; } @keyframes macro-ctp-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } /* 對話系統:自訂巨集<<speechBOX>> */ #speechBOX { padding: 20px; background-color: rgb(0 0 0 / 60%); border-radius: 20px; position: fixed; bottom: 2em; width: 75vw; transform: translateX(-49%); left: 51%; z-index: 5; } table.speechtable { width: 100%; } /* icon文字:SugarCube's built-in Font Awesome icons */ .sc-fa { font-family: tme-fa-icons; font-style: normal; font-weight: 400; font-variant: normal; text-transform: none; line-height: 1; speak: none; } .sc-icon { padding-left: 4px; } .sc-icon::before { font-family: tme-fa-icons; font-style: normal; font-weight: 400; font-variant: normal; text-transform: none; line-height: 1; speak: none; } .sc-caret-down::before { content: "\e818\00a0"; } .sc-caret-up::before { content: "\e81b\00a0"; } .sc-forward::before { content: "\e846\00a0"; } }}}
{{{ <!-- 對話系統 --> <<widget "speechBOX" container>> <<set _linklock to false>> <div id="speechBOX"> <table class='speechtable'> <tr> <<if $args.length > 2>> <td width="20vw" style="margin:1em;line-height: 0em;" class="ignore-br"> <img @src="$args[2]" width="200vw" class="center" style="position: relative;line-height: 0em;"> </td> <</if>> <td width="1%"></td> <td style="vertical-align:text-top;" class="ignore-br"> <h1 style="margin:0em;">$args[1]</h1> <hr style="margin:0.2em 0"> <p style="margin:0em;font-size:18px; line-height: 1.5em;" class="ignore-br">_contents</p> </td> <td width="5%" style="text-align:center;"> <<liveblock>><span id='back'><<if _linklock == false && _ctp.log.index > 0>><<link "<<scicon caret-up>>">><<ctpBack $back_dialog>><<update>><</link>><</if>></span><</liveblock>> <p> </p> <<liveblock>><span id='next'><<if _linklock == false && _ctp.log.index < _ctp.stack.length - 1>><<link "<<scicon caret-down>>">><<ctpAdvance $back_dialog>><<update>><</link>><</if>></span><</liveblock>> <p> </p> <<liveblock>><span id='next'><<if _linklock == false && _ctp.log.index == _ctp.stack.length - 1>><<link "<<scicon forward>>">><<goto $args[0]>><</link>><</if>></span><</liveblock>> </td> </tr> </table> </div> <</widget>> <!-- icon文字:SugarCube's built-in Font Awesome icons --> <<widget "scicon">><span @class="'sc-icon sc-' + $args.raw" alt="' + $args.raw + '"></span><</widget>> }}}
{{{ <!-- 影像地圖功能 --> /*! Image Map Resizer * Desc: Resize HTML imageMap to scaled image. * Copyright: (c) 2014-15 David J. Bradshaw - dave@bradshaw.net * License: MIT * Modified for use in Twine/SugarCube by: HiEv (2020) v1.0 */ (function() { function scaleImageMap() { function resizeMap() { function resizeAreaTag(cachedAreaCoords, idx) { function scale(coord) { var dimension = 1 === (isWidth = 1 - isWidth) ? "width" : "height"; return (padding[dimension] + Math.floor(Number(coord) * scalingFactor[dimension])); } var isWidth = 0; areas[idx].coords = cachedAreaCoords.split(",").map(scale).join(","); } if (image) { var scalingFactor = { width: $(image).width() / image.naturalWidth, height: $(image).height() / image.naturalHeight, }; var padding = { width: parseInt( window.getComputedStyle(image, null).getPropertyValue("padding-left"), 10 ), height: parseInt( window.getComputedStyle(image, null).getPropertyValue("padding-top"), 10 ), }; cachedAreaCoordsArray.forEach(resizeAreaTag); } } function getCoords(e) { // Normalize coord-string to csv format without any space chars return e.coords.replace(/ *, */g, ",").replace(/ +/g, ","); } function debounce() { clearTimeout(timer); timer = setTimeout(resizeMap, 250); } function start() { function setupMap () { if ($("img").width()) { resizeMap(); } else { if (++tries < 100) { setTimeout(setupMap, 20); } } } var tries = 0; if (image) { setupMap(); var imo = $(context).find(".imageMapObserve"); if (imo.length) { $(context).off("mouseup", ".imageMapObserve", resizeMap); imo.on("mouseup", resizeMap); if (window.ResizeObserver) { new ResizeObserver(debounce).observe($(context)[0].querySelector(".imageMapObserve")); } } } } function addEventListeners() { if (image) { image.addEventListener("load", resizeMap, false); // Detect late image loads in IE11 } window.addEventListener("focus", resizeMap, false); // Cope with window being resized whilst on another tab window.addEventListener("resize", debounce, false); window.addEventListener("readystatechange", resizeMap, false); document.addEventListener("fullscreenchange", resizeMap, false); $("#ui-bar-toggle").click(debounce); } function beenHere() { return "function" === typeof map._resize; } function getImg(name) { return $(context).find('img[usemap="' + name + '"]')[0]; } function setup() { areas = map.getElementsByTagName("area"); cachedAreaCoordsArray = Array.prototype.map.call(areas, getCoords); image = getImg("#" + map.name) || getImg(map.name); map._resize = resizeMap; // Bind resize method to HTML map element } var /* jshint validthis:true */ map = this, areas = null, cachedAreaCoordsArray = null, image = null, timer = null; if (!beenHere()) { setup(); addEventListeners(); start(); } else { var imo = $(context).find(".imageMapObserve"); if (imo.length) { $(context).off("mouseup", ".imageMapObserve", resizeMap); imo.on("mouseup", resizeMap); if (window.ResizeObserver) { new ResizeObserver(debounce).observe($(context)[0].querySelector(".imageMapObserve")); } } map._resize(); // Already setup, so just resize map } } function factory() { function chkMap(element) { if (!element.tagName) { throw new TypeError("Object is not a valid DOM element"); } else if ("MAP" !== element.tagName.toUpperCase()) { throw new TypeError("Expected <MAP> tag, found <" + element.tagName + ">."); } } function init(element) { if (element) { chkMap(element); scaleImageMap.call(element); maps.push(element); } } var maps; return function imageMapResizeF(target) { maps = []; // Only return maps from this call switch (typeof target) { case "undefined": case "string": Array.prototype.forEach.call(context.querySelectorAll(target || "map"), init); break; case "object": init(target); break; default: throw new TypeError("Unexpected data type (" + typeof target + ")."); } return maps; }; } var context = document; if (typeof define === "function" && define.amd) { define([], factory); } else if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(); // Node for browserfy } else { window.imageMapResize = factory(); } if ("jQuery" in window) { window.jQuery.fn.imageMapResize = function $imageMapResizeF() { context = this.prevObject; return this.filter("map").each(scaleImageMap).end(); }; } })(); $(document).on(":passagerender", function (event) { $(event.content).find("map").imageMapResize(); }); /* Image Map Resizer - End */ }}}
{{{ /* 影像地圖功能 */ /* Resizable element style */ .resizable { display: inline-block; overflow: hidden; line-height: 0; resize: both; } .resizable img { width: 100%; height: 100%; } /* Area element style */ area { cursor: pointer; } area:focus { outline: 2px solid black; } }}}
自訂巨集:{{{<<message>>}}} 生成一個連結,點擊後會展開新的內容,再點擊後會隱藏新的內容。 !!!!<<message '1.在「{ } Javascript」置入Javascript程式碼'>> @@color:green;<<include 'messagejs'>>@@ <</message>> !!!!<<message '2.在使用收合連結的Passage置入語法'>> @@color:green;{{{<<message [連結文字] [btn] [id]>>}}}@@收合內容@@color:green;{{{<</message>>}}}@@ * 連結文字(選填):點擊連結的文字內容,不填的話會顯示預設內容"展開/收合" * btn:增加此項目連結會變成按鈕 * id:選填,如果有多個"連結文字"相同但內容不同收合選單,則需要設定id。 <</message>> [[語法來源|https://twinelab.net/custom-macros-for-sugarcube-2/#/message-macro?id=macro-ltltmessagegtgt]] ---- 【實例】 <<message>>收合內容<</message>> <<return 返回>>
{{{ <!-- 自訂巨集<<message>>:收合選單 --> // message macro, by chapel (with help from T.M. Edwards); for sugarcube 2 // version 1.0.1 // see the documentation: https://github.com/ChapelR/custom-macros-for-sugarcube-2#message-macro //intialize namespace setup.messageMacro = {}; // default text option: setup.messageMacro.default = '展開/收合'; // <<message>> macro Macro.add('message', { tags : null, handler : function () { var message = this.payload[0].contents; var $wrapper = $(document.createElement('span')); var $link = $(document.createElement(this.args.includes('btn') ? 'button' : 'a')); var $content = $(document.createElement('span')); $link .wiki(this.args.length > 0 && this.args[0] !== 'btn' ? this.args[0] : setup.messageMacro.default) .ariaClick( this.createShadowWrapper( function () { if ($wrapper.hasClass('open')) { $content .css('display', 'none') .empty(); } else { $content .css('display', 'block') .wiki(message); } $wrapper.toggleClass('open'); })); $wrapper .attr('id', 'macro-' + this.name + '-' + this.args.join('').replace(/[^A-Za-z0-9]/g, '')) .addClass('message-text') .append($link) .append($content) .appendTo(this.output); } }); }}}
{{{ <!-- 自訂巨集<<notify>>:通知提醒 --> (function () { // notify.js, by chapel; for sugarcube 2 // version 1.1.1 // requires notify.css / notify.min.css var DEFAULT_TIME = 2000; // default notification time (in MS) var isCssTime = /\d+m?s$/; $(document.body).append("<div id='notify'></div>"); $(document).on(':notify', function (ev) { if (ev.message && typeof ev.message === 'string') { // trim message ev.message.trim(); // classes if (ev.class) { if (typeof ev.class === 'string') { ev.class = 'open macro-notify ' + ev.class; } else if (Array.isArray(ev.class)) { ev.class = 'open macro-notify ' + ev.class.join(' '); } else { ev.class = 'open macro-notify'; } } else { ev.class = 'open macro-notify'; } // delay if (ev.delay) { if (typeof ev.delay !== 'number') { ev.delay = Number(ev.delay); } if (Number.isNaN(ev.delay)) { ev.delay = DEFAULT_TIME; } } else { ev.delay = DEFAULT_TIME; } $('#notify') .empty() .wiki(ev.message) .addClass(ev.class); setTimeout(function () { $('#notify').removeClass(); }, ev.delay); } }); function notify (message, time, classes) { if (typeof message !== 'string') { return; } if (typeof time !== 'number') { time = false; } $(document).trigger({ type : ':notify', message : message, delay : time, class : classes || '' }); } // <<notify delay 'classes'>> message <</notify>> Macro.add('notify', { tags : null, handler : function () { // set up var msg = this.payload[0].contents, time = false, classes = false, i; // arguments if (this.args.length > 0) { var cssTime = isCssTime.test(this.args[0]); if (typeof this.args[0] === 'number' || cssTime) { time = cssTime ? Util.fromCssTime(this.args[0]) : this.args[0]; classes = (this.args.length > 1) ? this.args.slice(1).flatten() : false; } else { classes = this.args.flatten().join(' '); } } // fire event notify(msg, time, classes); } }); setup.notify = notify; }()); }}}
{{{ /* 自訂巨集<<notify>>:通知提醒 */ /* notify.min.css, by chapel; for use with notify.js version 1.0.0 */ #notify { position : fixed; display : block; width : 16em; right : -20em; top : 2em; padding : 0.5em; background-color : #fff; color : #000; -webkit-transition : right 0.3s; -moz-transition : right 0.3s; -o-transition : right 0.3s; transition : right 0.3s; } #notify.open { right : 0; } }}} :: notifyjs {"position":"1125,1000","size":"100,100"} {{{ <!-- 自訂巨集<<notify>>:通知提醒 --> (function () { // notify.js, by chapel; for sugarcube 2 // version 1.1.1 // requires notify.css / notify.min.css var DEFAULT_TIME = 2000; // default notification time (in MS) var isCssTime = /\d+m?s$/; $(document.body).append("<div id='notify'></div>"); $(document).on(':notify', function (ev) { if (ev.message && typeof ev.message === 'string') { // trim message ev.message.trim(); // classes if (ev.class) { if (typeof ev.class === 'string') { ev.class = 'open macro-notify ' + ev.class; } else if (Array.isArray(ev.class)) { ev.class = 'open macro-notify ' + ev.class.join(' '); } else { ev.class = 'open macro-notify'; } } else { ev.class = 'open macro-notify'; } // delay if (ev.delay) { if (typeof ev.delay !== 'number') { ev.delay = Number(ev.delay); } if (Number.isNaN(ev.delay)) { ev.delay = DEFAULT_TIME; } } else { ev.delay = DEFAULT_TIME; } $('#notify') .empty() .wiki(ev.message) .addClass(ev.class); setTimeout(function () { $('#notify').removeClass(); }, ev.delay); } }); function notify (message, time, classes) { if (typeof message !== 'string') { return; } if (typeof time !== 'number') { time = false; } $(document).trigger({ type : ':notify', message : message, delay : time, class : classes || '' }); } // <<notify delay 'classes'>> message <</notify>> Macro.add('notify', { tags : null, handler : function () { // set up var msg = this.payload[0].contents, time = false, classes = false, i; // arguments if (this.args.length > 0) { var cssTime = isCssTime.test(this.args[0]); if (typeof this.args[0] === 'number' || cssTime) { time = cssTime ? Util.fromCssTime(this.args[0]) : this.args[0]; classes = (this.args.length > 1) ? this.args.slice(1).flatten() : false; } else { classes = this.args.flatten().join(' '); } } // fire event notify(msg, time, classes); } }); setup.notify = notify; }()); }}}
生成一個包含數字0~9的數字密碼輸入介面,以下語法範例是將輸入的內容儲存到變數@@color:green;{{{$numlock}}}@@,並且在輸入密碼後進到新的Passage來觀看輸入結果。 !!!!<<message '1.在「# Stylesheet」置入CSS語法'>> @@color:green;<<include 'numlockcss'>>@@ <</message>> !!!!<<message '2.在使用數字密碼輸入的Passage置入語法'>> @@color:green;<<include 'numlockmacro'>>@@ <</message>> !!!!<<message '3.在判定結果的Passage置入語法'>> @@color:green; {{{ <<if $numlock is '密碼內容'>> 成功解鎖。 [[繼續|新的Passage]] <<else>> 輸入錯誤。 <<return 返回>> <</if>> }}} @@ <</message>> ---- 【實例】 <div id="numlock">\ @@color:DarkBlue;TYPE Password IN@@ <<nobr>> <<textbox "$numlock" "0" [[數字密碼判定]]>> <<set _keys = [1, 2, 3, 4, 5, 6, 7, 8, 9, "C", 0, "E"]>> <<set _txt = "<table id='numpad'><tr>">> <<for _i = 0; _i < _keys.length; _i++>> <<if typeof _keys[_i] === "number">> /* Create the number buttons. */ <<set _txt += '<td><<button "' + _keys[_i] + '">>'>> <<set _txt += '<<run $("#textbox-doorkey").val((parseInt($numlock, 10) * 10) + ' + _keys[_i] + ').trigger("change")>>'>> <<elseif _keys[_i] === "E">> /* Create the ENTER button. */ <<set _txt += "<td title=\"Enter\"><<button '<span style=\"font-size: 12px;\">◄┘</span>'>>">> <<set _txt += '<<goto "數字密碼判定">></td>'>> <<else>> /* Create the CLEAR button. */ <<set _txt += '<td title=\"Clear\"><<button "C">>'>> <<set _txt += '<<run $("#textbox-doorkey").val("0").trigger("change")>>'>> <</if>> <<set _txt += '<</button>></td>'>> <<if !((_i + 1) % 3) && (_i < 11)>> <<set _txt += "</tr><tr>">> <</if>> <</for>> <<set _txt += "</tr></table>">> _txt <</nobr>></div> @@color:green;輸入密碼後,點擊"◄┘"送出。如果輸入錯字,可點擊"C"清除。@@ <em>@@color:red;正確密碼:258789@@</em> <<return 返回>>
<<if $numlock is '258789'>> 成功解鎖。 [[繼續|start]] <<else>> 輸入錯誤。 <<return 返回>> <</if>>
{{{ /* 數字密碼輸入 */ #textbox-doorkey { width: 165px; min-width: auto; color:black; background-color: white; } #numlock { width: 170px; background-color: CornflowerBlue; text-align:center; vertical-align: middle; border-style:inset; } #numlock input { min-width: 140px; width: 88%; background-color: white; color: black; } #numpad td { width: 50px; } #numpad button { width: 50px; border-radius:10px; } }}}
{{{ <div id="numlock">\ @@color:DarkBlue;TYPE Password IN@@ <<nobr>> <<textbox "$numlock" "0" [[判定結果的Passage]]>> <<set _keys = [1, 2, 3, 4, 5, 6, 7, 8, 9, "C", 0, "E"]>> <<set _txt = "<table id='numpad'><tr>">> <<for _i = 0; _i < _keys.length; _i++>> <<if typeof _keys[_i] === "number">> /* Create the number buttons. */ <<set _txt += '<td><<button "' + _keys[_i] + '">>'>> <<set _txt += '<<run $("#textbox-doorkey").val((parseInt($numlock, 10) * 10) + ' + _keys[_i] + ').trigger("change")>>'>> <<elseif _keys[_i] === "E">> /* Create the ENTER button. */ <<set _txt += "<td title=\"Enter\"><<button '<span style=\"font-size: 12px;\">◄┘</span>'>>">> <<set _txt += '<<goto "判定結果的Passage">></td>'>> <<else>> /* Create the CLEAR button. */ <<set _txt += '<td title=\"Clear\"><<button "C">>'>> <<set _txt += '<<run $("#textbox-doorkey").val("0").trigger("change")>>'>> <</if>> <<set _txt += '<</button>></td>'>> <<if !((_i + 1) % 3) && (_i < 11)>> <<set _txt += "</tr><tr>">> <</if>> <</for>> <<set _txt += "</tr></table>">> _txt <</nobr>></div> }}}
自訂巨集:{{{<<preload>>}}} 遊戲開始前就先載入圖檔,避免遊戲新頁面內容載入完但圖片仍在載入。 要注意使用此巨集會增加遊戲啟動時間。 !!!!<<message '1.在「{ } Javascript」置入Javascript代碼'>> @@color:green;<<include 'preloadjs'>>@@ <</message>> !!!!<<message '2.在StoryInit中置入以下語法'>> @@color:green;{{{<<preload '圖檔1位置' '圖檔2位置'>>}}}@@ *如果載入的圖檔很多,可以建立bat執行檔(bat檔放在media資料夾中執行,就能自動匯出資料夾中所有jpg,jpeg,png,gif,bmp,tif,tiff格式的檔案)抓取圖檔位址,代碼如下: @@color:green; {{{ @echo off setlocal enabledelayedexpansion set "list=" for /r %%i in (*.jpg, *.jpeg, *.png, *.gif, *.bmp, *.tif, *.tiff) do ( if "%%~nxi" neq "list.bat" ( set "filename=%%~nxi" set "list=!list! 'media/!filename!'" ) ) echo ^<^<preload %list%^>^> > list.txt }}} @@ 建立bat執行檔方法: # 開啟記事本 # 複製貼上前述程式碼 # 另存檔案,檔名儲存為@@color:green;list.bat@@ # 把bat執行檔放到media資料夾中,再雙擊執行檔案 # 執行後會生成@@color:green;list.txt@@,複製txt中的內容到StoryInit即可 <</message>> [[語法來源|https://twinelab.net/custom-macros-for-sugarcube-2/#/preload]] <<return 返回>>
{{{ <!-- 自訂巨集<<preload>>:圖片預載 --> (function () { 'use strict'; // v1.0.0 // preload images prior to story startup function preloadImage (url, cb) { var img = new Image(); img.onload = cb; img.onerror = cb; img.src = url; } function preloadAll (urls, cb) { var loaded = 0; var toBeLoaded = urls.length; if (toBeLoaded === 0) { throw new Error("No URLs to preload!"); } urls.forEach( function (url) { preloadImage( url, function () { loaded++; if (loaded === toBeLoaded) { cb(); } }); }); } function isInit () { return State.length <= 0; } function preload () { var list = [].slice.call(arguments).flatten(); var id = isInit() ? LoadScreen.lock() : false; preloadAll( list, function () { if (id) { LoadScreen.unlock(id); } }); return id; } setup.preload = preload; setup.preload.force = false; /* <<preload imageUrls...>> */ Macro.add('preload', { handler : function () { if (!isInit() && !setup.preload.force) { return this.error("Attempting to preload images outside of `StoryInit` or similar can cause performance issues. Set `setup.preload.force` to `true` if you want to do it anyway."); } preload(this.args.flatten().filter( function (url) { return typeof url === 'string'; })); } }); }()); }}}
@@color:green;{{{<<replace "#id名稱">>取代內容<</replace>>}}}@@ 將特定id的{{{<div>或<span>}}}區塊,替換成取代內容的文字。 舉例來說,假設有個id為@@color:red;text@@的<span>區塊,要把該區塊內容替換成"新內容",完整的語法寫法如下: @@color:green; {{{ <span id="text">我是原本的內容</span> <<link "點擊連結替換內容">> <<replace "#text">>新內容<</replace>> <</link>> }}} @@ ---- 【實例】 <span id="dog">我是原本的內容</span> <<link "點擊連結替換內容">> <<replace "#dog">>新內容<</replace>> <</link>> [[重看一次|取代內容]] <<return 返回>>
以下是在twine中加入Google Fonts的中文網頁字型(思源黑體、思源宋體、楷體、圓體),以及內海字體的方法 * 新增CSS程式碼 @@color:green;<<include 'googlefontcss'>>@@ * 新增Javasceipt程式碼,使用[[webfont loader|https://hackmd.io/@oooooo/rypWLiz3?type=view]] google font的//思源黑體//、//思源宋體//是用url方式匯入,需要再用webfont loader方式才能在本機運行時也顯示字體 @@color:green;<<include 'googlefontjs'>>@@ * 各字體的CSS語法 # 使用思源黑體:@@color:red;{{{font-family:'Noto Sans TC', sans-serif !important;}}}@@ # 使用思源宋體:@@color:red;{{{font-family: 'Noto Serif TC', serif !important;}}}@@ # 使用圓體:@@color:red;{{{font-family: 'cwTeXYen', sans-serif !important;}}}@@ # 使用楷體:@@color:red;{{{font-family: 'cwTeXKai', sans-serif !important;}}}@@ # 使用內海體:@@color:red;{{{font-family: 'naikaifont', sans-serif !important;}}}@@ ---- 【實例】 <span style="font-family:'Noto Sans TC', sans-serif !important;">思源黑體</span> <span style="font-family: 'Noto Serif TC', serif !important;">思源宋體</span> <span style="font-family: 'cwTeXYen', sans-serif !important;">圓體</span> <span style="font-family: 'cwTeXKai', sans-serif !important;">楷體</span> <span style="font-family: 'naikaifont', sans-serif !important;">內海體</span> <<return 返回>>
{{{ <!-- 匯入google字體(思源黑體、思源宋體):webfont loader --> importScripts("https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js") .then(function () { // Code that depends on the script goes here. WebFont.load({ google: { families: ["Noto Sans TC&display=swap", "Noto Serif TC&display=swap"] } }); }) .catch(function (err) { // There was an error loading the script, log it to the console. console.log(err); }); }}}
{{{ /* 匯入google字體 & 內海字體 */ /* 思源黑體、思源宋體 */ @import url("https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@300&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@400&display=swap"); /* 楷體字體 https://fonts.googleapis.com/earlyaccess/cwtexkai.css */ @font-face { font-family: 'cwTeXKai'; font-style: normal; font-weight: 500; src: url(https://fonts.gstatic.com/ea/cwtexkai/v3/cwTeXKai-zhonly.eot); src: url(https://fonts.gstatic.com/ea/cwtexkai/v3/cwTeXKai-zhonly.eot?#iefix) format('embedded-opentype'), url(https://fonts.gstatic.com/ea/cwtexkai/v3/cwTeXKai-zhonly.woff2) format('woff2'), url(https://fonts.gstatic.com/ea/cwtexkai/v3/cwTeXKai-zhonly.woff) format('woff'), url(https://fonts.gstatic.com/ea/cwtexkai/v3/cwTeXKai-zhonly.ttf) format('truetype'); } /* 圓體字體 https://fonts.googleapis.com/earlyaccess/cwtexyen.css */ @font-face { font-family: 'cwTeXYen'; font-style: normal; font-weight: 500; src: url(https://fonts.gstatic.com/ea/cwtexyen/v3/cwTeXYen-zhonly.eot); src: url(https://fonts.gstatic.com/ea/cwtexyen/v3/cwTeXYen-zhonly.eot?#iefix) format('embedded-opentype'), url(https://fonts.gstatic.com/ea/cwtexyen/v3/cwTeXYen-zhonly.woff2) format('woff2'), url(https://fonts.gstatic.com/ea/cwtexyen/v3/cwTeXYen-zhonly.woff) format('woff'), url(https://fonts.gstatic.com/ea/cwtexyen/v3/cwTeXYen-zhonly.ttf) format('truetype'); } /* 內海字體 https://github.com/max32002/naikaifont */ @font-face { font-family: 'naikaifont'; src: url(https://cdn.jsdelivr.net/gh/max32002/naikaifont@1.0/webfont/NaikaiFont-Regular-Lite.woff2) format("woff2"), url(https://cdn.jsdelivr.net/gh/max32002/naikaifont@1.89/webfont/NaikaiFont-Regular.woff2) format("woff2"); } }}}
1. 使用SugarCube內建函數@@color:green;{{{previous()}}}@@ @@color:green;{{{[[返回|previous()]]}}}@@ 2. 使用@@color:green;{{{<<return>>}}}@@巨集 @@color:green;{{{<<return 返回>>}}}@@ 3. 使用@@color:green;{{{$return}}}@@變數 這個方法只在進入新的Passage後才生效,無法返回遊戲開始的Passage。 不過此方法可以透過增加標籤「noreturn」的方式,將不希望返回的passage給排除。 * 在「{ } Javascript」置入以下程式碼: > <div>@@color:green; {{{ <!-- 返回前頁:紀錄於變數$return --> $(document).on(':passagestart', function (ev) { if (!ev.passage.tags.includes('noreturn')) { State.variables.return = ev.passage.title; } }); }}} @@</div> * 在Passage中使用語法:@@color:green;{{{<<link '返回' $return>><</link>>}}}@@ ---- 【實例】 @@color:green;{{{previous()}}}@@:[[返回|previous()]] @@color:green;{{{<<return>>}}}@@:<<return 返回>> @@color:green;{{{$return}}}@@:<<link '返回' $return>><</link>>