panda3d创建一个避免点到底层按钮的遮罩

编写游戏的时候经常要用到弹出游戏内对话框窗口什么的
而在弹出窗口时要禁用底层窗口的元素怎么办呢……
于是大地想到了一个跟HTML类似的方法,在窗口和下面之间加一个全透明遮罩层
遮罩层用DirectButton就能解决会按到下面的按钮的问题了

但是新的问题来了,在下面加DirectButton的话因为PGButton的特性会阻断Panda3d的事件
所以滚轮什么的也不能在弹出窗口用了

那么……只要让这个遮罩层能够重新发送滚轮和其他鼠标事件就可以了!

于是

#-*- coding:utf-8 -*-
'''
====================================================================================

   Copyright 2013, 2014 Windy Darian (大地无敌), Studio "Sekai no Kagami" 
   (世界之镜制作组) of Seven Ocean Game Arts (七海游戏文化社
   , 北京航空航天大学学生七海游戏文化社) @ http://sogarts.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================================
Created on Apr 6, 2013
dialog mask
@author: Windy Darian
'''

from panda3d.core import NodePath,PGButton,MouseButton
from direct.showbase.DirectObject import DirectObject
import direct.gui.DirectGuiGlobals as DGG
from direct.gui.DirectButton import DirectButton

class DialogMask(DirectButton):
    '''used to generate a full-screen mask to prevent button-clicking below the focused window/dialog
    Added some tricks to make panda3d mouse events still available
    '''
    B1PRESS = PGButton.getPressPrefix() + MouseButton.one().getName() + '-'  
    B2PRESS = PGButton.getPressPrefix() + MouseButton.two().getName() + '-'  
    B3PRESS = PGButton.getPressPrefix() + MouseButton.three().getName() + '-'
    B4PRESS = PGButton.getPressPrefix() + MouseButton.four().getName() + '-'
    B5PRESS = PGButton.getPressPrefix() + MouseButton.five().getName() + '-'
    WHEELUP = PGButton.getReleasePrefix() + MouseButton.wheelUp().getName() + '-'
    WHEELDOWN = PGButton.getReleasePrefix() + MouseButton.wheelDown().getName() + '-'
    WHEELLEFT = PGButton.getReleasePrefix() + MouseButton.wheelLeft().getName() + '-'
    WHEELRIGHT = PGButton.getReleasePrefix() + MouseButton.wheelRight().getName() + '-'
    B1RELEASE = PGButton.getReleasePrefix() + MouseButton.one().getName() + '-'  
    B2RELEASE = PGButton.getReleasePrefix() + MouseButton.two().getName() + '-'  
    B3RELEASE = PGButton.getReleasePrefix() + MouseButton.three().getName() + '-'
    B4RELEASE = PGButton.getReleasePrefix() + MouseButton.four().getName() + '-'
    B5RELEASE = PGButton.getReleasePrefix() + MouseButton.five().getName() + '-'
    
    def __init__(self):
        DirectButton.__init__(self,parent = aspect2d, frameColor =(1,1,1,0), relief = DGG.FLAT,commandButtons = [])
        self.accept('window-event', self.windowResize)
        self.windowResize(None)
        #trick: make this re-throw mouse events
        self.bind(self.B1PRESS, self.rethrowEvent,['mouse1'])
        self.bind(self.B2PRESS, self.rethrowEvent,['mouse2'])
        self.bind(self.B3PRESS, self.rethrowEvent,['mouse3'])
        self.bind(self.B4PRESS, self.rethrowEvent,['mouse4'])
        self.bind(self.B5PRESS, self.rethrowEvent,['mouse5'])
        
        self.bind(self.WHEELUP, self.rethrowEvent, ['wheel_up'])
        self.bind(self.WHEELDOWN, self.rethrowEvent, ['wheel_down'])
        self.bind(self.WHEELLEFT, self.rethrowEvent, ['wheel_left'])
        self.bind(self.WHEELRIGHT, self.rethrowEvent, ['wheel_right'])
        
        self.bind(self.B1RELEASE, self.rethrowEvent,['mouse1-up'])
        self.bind(self.B2RELEASE, self.rethrowEvent,['mouse2-up'])
        self.bind(self.B3RELEASE, self.rethrowEvent,['mouse3-up'])
        self.bind(self.B4RELEASE, self.rethrowEvent,['mouse4-up'])
        self.bind(self.B5RELEASE, self.rethrowEvent,['mouse5-up'])
        
    def windowResize(self,arg):
        #Make this mask fill the screen
        self.reparentTo(aspect2d,sort = self.getSort())
        aspect = base.getAspectRatio()
        if aspect > 1:
            self['frameSize'] = (-aspect,aspect,-1,1)
        elif aspect: 
            hh = 1.0/aspect
            self['frameSize'] = (-1,1,-hh,hh)
            
    def setCommandButtons(self, *args, **kwargs):
        #inherited 
        pass
        
    def rethrowEvent(self,sevent,event):
        messenger.send(sevent)
        
                
    def destroy(self):
        self.ignoreAll()

在自定义窗体中,只要使用这个DialogMask类,在自定义窗体或对话框显示出时执行mask.show(),消失是执行mask.hide()就可以了

鼠标能正常点到前面的按钮,但点不到底层的按钮,鼠标滚轮滚动的事件也正常
鼠标能正常点到前面的按钮,但点不到底层的按钮,鼠标滚轮滚动的事件也正常

基于Panda3D的Galgame引擎/框架——Sogal开发中

这是大地无敌自己开发供自己的小伙伴们和北航七海游戏文化社用的galgame引擎——SOGAL(Seven Ocean Galgame Engine)
为什么要自己做呢?
因为大地的需求是要实现3D战斗场景的支持和一些2D3D混合的故事场景
用现成的gal引擎不太现实
于是选择了基于panda3d这个3D引擎,在这基础上再开发一个Galgame引擎
脚本控制示例
于是……今天脚本控制功能也实现了
脚本语言当然是python以及在这个基础上便于galgame流程制作的一套自定义剧本格式

GitHub的Repo在https://github.com/WindyDarian/Sogal
基于Apache License V2.0授权 基本上只要保留了版权声明和证书的原文件大家都可以用的说!

基本上再有主菜单选择枝存档之类的就可以成为第一版了
(但是果然还是好艰巨……

(当然预计的3D战斗系统并不算在这里面而是游戏的另外一个部分所以Apache License V2.0只限于这个框架本身。至于游戏本体用什么授权还没想好(逃

Panda3d 支持淡入淡出(Fading)的音频管理模块和类

嘛 因为需求音频的淡入淡出 但Panda3d默认不支持淡入淡出
于是就在Panda3d的基础上稍微写了一个支持淡入淡出的音频播放器模块
用了一些panda的Intervals,淡入淡出的效果还不错
还默认使用了loader后台读取的特性:)

from runtime_data import game_settings是窝的游戏设置,把相关的东西改成自己的后就可以直接用了!
AudioPlayer在Panda3D的ShowBase初始化之后再实例化即可

audio_player.py

#-*- coding:utf-8 -*-
'''
====================================================================================

   Copyright 2013, 2014 Windy Darian (大地无敌), Studio "Sekai no Kagami" 
   (世界之镜制作组) of Seven Ocean Game Arts (七海游戏文化社
   , 北京航空航天大学学生七海游戏文化社) @ http://sogarts.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================================
Created on Jul 29, 2013
Audio manager.
@author: Windy Darian (大地无敌)@ http://www.agrp.info 
'''
from panda3d.core import AudioSound,AudioManager
from direct.showbase.DirectObject import DirectObject
from direct.interval.LerpInterval import LerpFunc
from direct.interval.FunctionInterval import Func,Wait
from direct.interval.IntervalGlobal import Sequence
from direct.stdpy.file import exists    #use panda3d's file API to support panda's virtual folder

from runtime_data import game_settings    #spec for my game

_fadeinIntervalTable = {}
_intervals = []

def play_audio(audio,fadein = 0, volume = 1, loop = False):
    '''plays an AudioSound'''
    if loop:
        audio.setLoop(True)
    if not fadein:
        audio.setVolume(volume)
        audio.play()
    else:
        interval = LerpFunc(_lerpAdjust, duration = fadein,
                            fromData = 0, toData = volume,
                            extraArgs = , blendType = 'easeOut')
        _fadeinIntervalTable = interval   
        _intervals.append(interval)    
            #save the interval in the table so if the audio stops during the interval it can stop the interval first
        audio.play()
        interval.start()
        
    
def stop_audio(audio,fadeout = 0):
    '''stops an AudioSound, instant or fade-out'''
    if _fadeinIntervalTable.has_key(audio):     #If the audio is currently on fade-in, then stop and remove the fade-in interval
        _fadeinIntervalTable.pause()
        _intervals.remove(_fadeinIntervalTable)
        del _fadeinIntervalTable
           
    if not fadeout:
        audio.stop()
    else:
        sequence = Sequence(LerpFunc(_lerpAdjust, duration = fadeout,
                                     fromData = audio.getVolume(), toData = 0,
                                     extraArgs = , blendType = 'easeIn'),
                            Func(audio.stop)
                            )
        sequence.start()  #reduce volume and stop the sound
        
        
def play(filepath, manager, loop = False, fadein = 0, volume = 1):
    '''load a sound in background, and play it when loading finishes'''
    loader.loadSound(manager,
                     filepath,
                     callback = play_audio,extraArgs = [fadein, volume, loop])    
    

def _lerpAdjust(volume,audio):
    '''Adjust the volume dynamiclly in fadein and fadeout'''
    audio.setVolume(volume)
    
_bgmMgr = None
_envMgr = None
_voiceMgr = None
_sfxMgr = None

class AudioPlayer(DirectObject):
    
    '''Audio Player class of Sogal
    usually there only need 1 instance
    '''
    def __init__(self):
        DirectObject.__init__(self)
        
        global _bgmMgr,_envMgr,_sfxMgr,_voiceMgr    
        '''if we already has an AudioPlayer instance,
           we just need to refer to the existing AudioManagers
           insead of creating some new ones'''
        if not _bgmMgr:
            _bgmMgr = AudioManager.createAudioManager()          #don't use ShowBase.musicManager for it can only play 1 music at the same time defaultly so it don't support cross-fade
            base.addSfxManager(_bgmMgr)
        if not _envMgr:
            _envMgr = AudioManager.createAudioManager()
            base.addSfxManager(_envMgr)
        if not _voiceMgr:
            _voiceMgr = AudioManager.createAudioManager()
            base.addSfxManager(_voiceMgr)
        if not _sfxMgr:
            _sfxMgr = AudioManager.createAudioManager()
            base.addSfxManager(_sfxMgr)
            
        self.bgmMgr = _bgmMgr
        self.envMgr = _envMgr
        self.voiceMgr = _voiceMgr
        self.sfxMgr = _sfxMgr 
        
        self.applySettings()
        
        self._currentBGM = None #to define there can only be one bgm
        self._currentENV = None # and one environment sound at the same time
        
        '''#test1 Load and play a music
        loader.loadSound(self.bgmMgr,
                         'audio/music/God-only-knows-Secrets-of-the-Goddess-.wav',
                         callback = play_audio,extraArgs = [3,1])
        '''
        '''#test2 A fade-out stop before the fade-in is completed
        sound = loader.loadSound(self.bgmMgr,'audio/music/God-only-knows-Secrets-of-the-Goddess-.wav')
        sq = Sequence(Func(play_audio,sound,10),Wait(5),Func(stop_audio,sound,5))
        sq.start()
        '''
        '''#test3  Load and play a music
        self.playBGM('church-organ-music', 2, 1, True)
        '''
        '''#test4 Another music before the fade-in is completed
        sq = Sequence(Func(self.playBGM,'audio/music/God-only-knows-Secrets-of-the-Goddess-.wav',10),
                      Wait(5),
                      Func(self.playBGM,'audio/music/church-organ-music.wav',10),
                      )
        sq.start()
        '''
        
    def applySettings(self):
        self.bgmMgr.setVolume(game_settings['music_volume']) #0.75
        self.envMgr.setVolume(game_settings['env_volume']) #0.75
        self.voiceMgr.setVolume(game_settings['voice_volume']) #1
        self.sfxMgr.setVolume(game_settings['sfx_volume']) #1
        
        
    def playVoice(self, path, volume = 1):    
        pathes = game_settings['voicepathes']
        types = game_settings['soundtypes']
        for ft in ((folder,type) for folder in pathes for type in types):
            if exists(ft[0] + path + ft[1]):
                path = ft[0] + path + ft[1]
                break
        play(path, self.voiceMgr, volume = volume)
    
    def playSound(self, path, volume = 1):
        pathes = game_settings['sfxpathes']  #example: ['audio/music','audio','']
        types = game_settings['soundtypes']  #example: ['.wav','.ogg','']
        for ft in ((folder,type) for folder in pathes for type in types):
            if exists(ft[0] + path + ft[1]):
                path = ft[0] + path + ft[1]
                break
        play(path,self.sfxMgr, volume = volume)
        
    def playBGM(self, path, fadein = 0, volume = 1, loop = True):
        if self._currentBGM:    #if there is a music playing then stop it
            self.stopBGM(fadein)
        pathes = game_settings['musicpathes']
        types = game_settings['soundtypes']
        for ft in ((folder,type) for folder in pathes for type in types):
            if exists(ft[0] + path + ft[1]):
                path = ft[0] + path + ft[1]
                break
        loader.loadSound(self.bgmMgr,
                     path,
                     callback = self._setAndPlayBGM,extraArgs = [fadein, volume, loop])    
    
    def playENV(self, path, fadein = 0, volume = 1, loop = True):
        if self._currentENV:
            self.stopENV(fadein)
        pathes = game_settings['envsoundpathes']
        types = game_settings['soundtypes']
        for ft in ((folder,type) for folder in pathes for type in types):
            if exists(ft[0] + path + ft[1]):
                path = ft[0] + path + ft[1]
                break
        loader.loadSound(self.envMgr,
                     path,
                     callback = self._setAndPlayENV,extraArgs = [fadein, volume, loop]) 
        
    def stopBGM(self, fadeout = 0):
        if self._currentBGM:
            stop_audio(self._currentBGM, fadeout)
        
    def stopENV(self, fadeout = 0):
        if self._currentENV:
            stop_audio(self._currentENV, fadeout)
    
    def stopVoice(self):
        self.voiceMgr.stopAllSounds()
    
    def stopSound(self):
        self.sfxMgr.stopAllSounds()
    
    def _setAndPlayBGM(self, audio, fadein, volume, loop):
        self._currentBGM = audio
        play_audio(audio, fadein, volume, loop)
        
    def _setAndPlayENV(self, audio, fadein, volume, loop):
        self._currentENV = audio
        play_audio(audio, fadein, volume, loop)
        
    

        

……嘛继续填坑去

Panda3D中会随窗口大小改变而改变宽高比的Camera实现方式(如何创建一个默认base.cam那样的Camera)

panda3d使用base.makeCamera(base.win)创建的Camera并不会随着窗体大小的改变而改变宽高比……于是到底怎样实现这样的Camera呢?
[省略一万字的口胡]
于是……这样就可以了……
在Showbase或者某个自定义的DirectObject中

from panda3d.core import Camera, Lens

创建相机时

self.cam1 = base.makeCamera(base.win)            #生成Camera
self.lens = PerspectiveLens()                    #普通的透视镜头
self.lens.setMinFov(50.534016)                   #设置短边的Field of View这时候如果宽高比是16:9的话长边搞好是80度,大地喜欢。
                                                 #因为有些时候窗体会被拉成高比宽要大的情况所以这里应该设置MinFov而不是默认的宽的FOV
self.lens.setAspectRatio(base.getAspectRatio())  #根据画面设置宽高比


#...其它的一些设置

self.accept('window-event', self._adjustAspectRatio)  #然后,要响应窗体大小改变的事件创建Panda3D的任务(说明文档里没有这个事件啊!好不容易才在panda3d的论坛里发现的说……)

#....嘛

用于响应事件的方法

def _adjustAspectRatio(self,arg):
    if self.camera:
        self.lens.setAspectRatio(base.getAspectRatio())  #动态改变宽高比

于是这样自定义camera就成功模仿了默认camera的窗体大小改变而不失真的效果了!XD

今天做Gal模式场景的管理 本来只想就像标准Gal一样做个2D场景与立绘的
然后一时兴起在透视Camera实现了……(嗯同时解决了上面的问题)嗯 于是就变成了表面是2D其实是3D(这不蛋疼吗!)
嘛,既然Panda3D是3D引擎那当然要有除了脑补中的3D战斗外更多地利用它的优点的地方
于是它变成了很容易混合2D和3D的界面的奇怪的东西了……
撒花!

tttt

嘛,另外不要问窝为什么在坑引擎……(望天,不如说第二阶段终于开始了吧

啊啊啊啊啊啊啊啊啊忘了德叔的坑了!!!!!!!!!!!!!!!!

python中用exec执行脚本命令的变量可访问性 (python3.3)

在python中写游戏引擎的时候经常会用到exec执行脚本命令和getattr动态调用函数
但是……使用exec执行脚本命令时,exec对外界变量的可访问性如何?外界对exec中的变量的可访问性又如何?

经过本大地一个简单的小测试后,大概结论如下
结论1:exec在最外层时后续的代码可以访问其中声明的变量,可以调用外部的函数
结论2:exec在最外层时在其中对变量进行的修改对外部有效
结论3:在函数中使用exec,对于简单的数据类型使用了值传递的方式,即exec中进行声名和修改对外部无效;但是全局变量的声明和修改是有效的;其定义的变量能被后面的exec访问但不能被本身的函数体访问
结论4:在类成员函数中exec可以定义、访问和修改类的成员;但是其中定义的普通变量只能被后面的exec访问(喵的好神奇!)
结论5:exec在最外层时后续的代码可以访问其中声明的函数和类

然后以下是测试过程

# -*- coding:utf-8 -*-
”’
Created on Jun 28, 2013
python中用exec执行脚本命令的变量可访问性的小测试 (python3.3)
@author: 大地无敌 – http://www.agrp.info
”’
 
#1、在最外层执行exec声明变量
exec (‘test = ‘miao” )
print (test ) #输出miao
 
#结论1:exec在最外层时后续的代码可以访问其中声明的变量,可以调用外部的函数
 
 
#2、普通修改和函数调用的测试
def plus( mm):
    return mm + ‘World!’
= ‘Hello ‘
exec (‘k = plus(k)’ )
print (k ) #输出Hello World!
 
#结论2:exec在最外层时在其中对变量进行的修改对外部有效
 
#3、在函数中使用exec
def main():
    test2 = 1
    exec (‘test2 = test2+233nabc=3nprint(test2)’ ) #输出的是234
    exec (‘print(test2)’ ) #输出的是1
    exec (‘global test3ntest3 = test2’ )
    exec (‘test4 = ‘How about this?” ) #输出How about this?
    exec (‘print(test4)’ )
    print (test2 ) #1而不是2,说明在函数中用exe没有改变函数中的变量
    print (test3 ) #输出的是1
    #print(test4) #会认为test4未定义
 
main ()
#print(test2) #会认为test2未定义,说明在函数中用exec访问的不是模块中的变量
print (test3 ) #输出的是1
 
#结论3:在函数中使用exec,对于简单的数据类型使用了值传递的方式,即exec中进行声名和修改对外部无效;但是全局变量的声明和修改是有效的;其定义的变量能被后面的exec访问但不能被本身的函数体访问
 
#4、类(构造)函数中的测试
class Test2:
    def __init__ (self ):
        exec ( “self.age = 20”)
        exec ( “abc = 65535”)
        exec ( “print(abc)”) #65535
        #print(abc)  #会认为abc未定义
 
test2 = Test2 ()
print (test2 .age ) #输出的是20
#print(abc) #会认为abc未定义
#exec(“print(abc)”) #会认为abc未定义
 
#结论4:在类成员函数中exec可以定义、访问和修改类的成员;但是其中定义的普通变量只能被后面的exec访问(喵的好神奇!)
 
#5、在exec中定义函数和类
exec (‘def test():n    print(‘hahahaha’)n’ )
test () #输出hahahaha
exec (‘class MyObject():n    def __init__(self):n        print(‘OKOK!’)n    def test(self):n        print(‘Hello’)’ )
myObject = MyObject () #OKOK
myObject .test () #Hello
 
#结论5:exec在最外层时后续的代码可以访问其中声明的函数和类
 
 
input (“nya!” )
”’
在会出现异常的代码都注释掉后的最终输出如下:
miao
Hello World!
234
1
How about this?
1
1
1
65535
20
hahahaha
OKOK!
Hello
nya!
”’

 

注意 在Python2.7 中 结果是

miao
Hello World!
234
234
How about this?
234
234
234
65535
20
hahahaha
OKOK!
Hello
nya!

也就是说在函数中使用exec对简单变量进行的声明和修改在python2.7中会反映到外界

温蒂达利安

温蒂达利安

ウィンディーダリアン | 大地無敵 [pixiv] http://www.pixiv.net/member_illust.php?mode=medium&illust_id=36589663

温蒂达利安。
手持大剑的守序邪恶面向对象幻术师“黑色温蒂”,妄想的饕餮。
以“大地无敌”为代号,进行各种奇怪的计划。
虽然奇怪的计划大概在有条不紊地进行中吧,
还是因为拖延症的原因才是得它们看上去是有条不紊地进行中的?

于是这个图顺便作为温蒂酱搬宿舍的纪念物好了……

再见沙河

这只是一个城市的一个高校到了某个年级要搬迁校区的学长的常见心境而已。——突然发现还有大半个月就要和这个地方说再见了。刚好某形式与政策课的论文也快到截止期限了,于是借着这样的风说说感想吧。

首先想到的大概是那个社团。某燃君在社团内部共享了一个告别沙河的自制文字冒险游戏,到现在只觉得是昨天的事情而已——其实那已经是一年前了。而我也将追随某燃脚步去。虽然好不容易建立起来的一些东西会成为回忆,不过有了回忆的羁绊大概才会更棒。再说这个社团,本来就是基本每个学期只需要见面一次的在线类型社团不是吗?即使人没有在一起,大家也能够像往常一样的一起努力不是吗?——本来想说这样就能满足了,其实一想还是完全不够啊!

123于是这个是去年某燃的随笔游戏

沙河真的是个很好的地方,除了一些课程和要求不尽人意之外,真是一个世外桃源。至少风景算是上品。只要有学生卡在手,就可以一个学期不出校门。食堂三楼的翔锅还没吃够,二楼的肥牛饭和鸡排饭比一楼更引人回味,而一楼早上的煎饼真是百吃不厌,油条也是最近才发现……我究竟是从什么时候开始喜欢上一个学校的食堂的饭菜?——你们要是想在食堂三楼的香锅场上战个痛记得叫上我啊!我会乘着4点半的校车赶来!

刚刚从物理实验室出来。这大概就是最后了。——两年沙河,一年物理,一年物理实验。本来以为再也不用做实验了是一件很令人愉快的事,但这种感觉是随着解放感而至的感伤吗?——说到遗憾的话也还有,毕竟我还没有在久负盛名的李老师的带领下做过实验。虽然在某种意义上大概明年能见到的可能性也存在吧……

还有一个伤心的地方是,再也不能见到野生的学妹!不过想想那群丧心病狂的助教,心里也就平衡了。嘛,这个话题还是不要展开比较好。不过,学长我只能帮到这里了。

或许某棵路旁的树下的铭牌还会留下我们存在过的印记。但是过不了多久,它也会换成新的铭牌吧。

宿舍。那边的宿舍,估计也不会有这边这样的感觉了——毕竟这是我第一次在名为“家”的地方意外开辟的据点——还有些连“家”也比不上的地方。住在这个宿舍的下一位会是什么样的人?在把最后一件物品打包之前,我会留下一张纸条什么的。因为我不是很在意细节问题,所以“留下这么多无法擦除的污渍真是对不起了。”大概会这样写道。——留下这么多无法擦除的回忆真是对不起了。

不过就算再怎么不舍,这一切也都会成为过去时。所以,让我们怀揣过去,迎接崭新的未来——

——你以为我会这样说吗?如果不是被迫的话,我还想在这个鸟不拉屎能够仰望星空的地方留上个三五年!翔锅还没战够,你以为这么简单就能把我轻易抹去?如果不是被逼的话我才不会想把我占据了两年的据点轻易地让给另一个人!虽然像是在说傻话一般,不能每天早上穿着拖鞋在名叫TD线的被早上的太阳晃瞎眼的生活怎么能接受!所以说——所以说!

我一定会回来的!

——虽然感觉做出了很不好的预言的样子。

 

456

最后附上一个半成品的做为留恋的半成品图吧。

嘛,虽然被拖延症所困,但是大地无敌和大家也不会就这样坑掉的哦。