td里面字体大小怎么改_TD 杂谈09 | 使用Script CHOP做一条贪吃蛇

07.2017-02.2020 Use Tool:Touchdesigner Author:第五杂谈会 Touchdesigner Pattern & Plugin -

写在比前面还前面的话:

TD 杂谈08 | 中的LidarTracker工具测试试用时间已经到啦,十分感谢在测试期间使用LidarTracker的各位朋友,感谢各位在测试期间的反馈。如果日后有需要使用LidarTracker工具的话,请与我们联系。如果你在使用Touchdesigner中有任何需求需要做成工具,也欢迎与我们联系。

另外,新版本的LidarTracker我们也在计划中,如果你对在Touchdesigner中使用雷达有一些希望做到的功能的话,可以向我们建议噢,我们会考虑加入开发内容。


- 写在前面的废话 -
  • 为什么做了个贪吃蛇

    主要是因为作者最近沉迷switch不能自拔,摸到键盘就想起游戏,正好带着个小朋友,就做个贪吃蛇教小朋友,顺便让小朋友也体会一下打游戏的乐趣。4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png

    这次的贪吃蛇主要是用代码实现的主要逻辑,所以对于TD脚本写不好的朋友,你们可以稍微紧张的往下看。4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png

8e10f62c86e9c12402e4efc90308bcb3.png

所有元件的全家福 首先奉上小编制作贪吃蛇程序的录屏,小编制作这个程序花了两个多小时,视频加速了12倍,虽然具体怎么制作可能不怎么能看出来,但是还是可以大致看出来制作思路的。有兴趣的朋友先可以看看。4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png4d2106f6086033e7b52ab631c314ec5d.png

假装这是一条注释

- 制作思路 -
  • 治大国写程序如烹小鲜

    就像做菜一样,首先得要有菜谱(程序逻辑),然后需要有食材与锅碗瓢盆(数据与运行环境)。准备好这些要素,能才能开火炒菜(写脚本)。

  • 程序逻辑贪吃蛇其实并不复杂,游戏程序共3种状态,待机,游戏中,游戏结束,游戏程序始终在这3种中的一个状态运行。

    在待机状态,我这里设定需要显示一个待机画面并等待键盘里的方向键按下。在方向键按下之后,待机状态解除,进入游戏状态,小蛇就开始它变成巨蟒的道路,在游戏中的状态时候,如果发生小蛇碰撞到自己身体或者小蛇已经成长到屏幕都容不下的时候,游戏结束,进入游戏结束状态,游戏结束的时候像素点从下往上依次点亮,满屏之后回到待机状态。

46bce2daa87cd69b19bfe0420c7ffe96.gif

成品

  • 数据与运行环境这个程序主要画面的渲染我是使用GeometryCOMP的Instance方法实现的,对Instance方法不熟悉的朋友可以移步TEA社区的中文视频教程,里面有介绍。
http://www.touchdesigner.co/intermediate
TEA社区中级TD教程网址

5cff8d2592a4fdacf195a3771a2e73d4.png

通过Grid SOP生成点阵数据

一个简单的思路就是,使用GridSOP生成一个矩形点阵数据,使用GeometryCOMP的Instance方法将点阵渲染出来,至于贪吃蛇的操作逻辑,我就可以通过操作点阵中的每个点的scale与RGBA值来实现。

c008cdd9e61d73003639c324b4409e73.png

通过GeometryCOMP的Instance方法将点阵渲染出来

有了渲染画面的方法之后,接下来就是如何通过ScriptCHOP来生成数据的问题了。

为了使用ScriptCHOP生成数据,我们必须将一些需要定义的数据提前准备好,比如用于控制程序状态的gameState,用于控制小蛇前进方向的snakeDiraction等等。这些数据我都使用了ConstantCHOP来生成。

f30d5fe7b5378870ce27a425f7374ba3.png

用于定义数据的ConstantCHOP

除此之外,还需要一个储存在游戏中小蛇不断变长的身体数据的地方。这里我使用了一个TableDAT,TableDAT脚本中的appendRow()方法和clear()方法可以比较方便的操作数据。

9f279e3f886aa9a509f3674facb65977.png

储存小蛇身体数据的节点,使用DattoCHOP转化为CHOP节点是为了方便ScriptCHOP获取数据

有了以上这些基础的数据之后,我们还需要一个触发器,用于更新小蛇前进的速度,这里的触发器我是用LFOCHOP制作。

d072aff39c55525db836db965b167859.png

游戏的速度等级可以通过调整LFO的Frequency属性调整

准备好以上节点之后,就可以将以上节点连入ScriptCHOP,开始编写ScriptCHOP脚本生成我们想要的数据了。

948159082b22ae76b44b13293cb6a1f6.png

TD代码写得效率高不高不重要,重要是界面一定要整齐(手动狗头)

  • 写脚本

    数据都准备完成了,接下来就是进入scriptCHOP写脚本了。完整的脚本如下:

def onSetupParameters(scriptOp):  # 生成初始化按钮  page = scriptOp.appendCustomPage('Game Control')  p = page.appendPulse('Init', label='Initialize Game')  return  def onPulse(par):  if par.name == 'Init' :    # 定义所需OP    dataUpdateOp = op('dataUpdate')    controlConsOp = op('controlCons')    snakeDataTableOp = op('snakeDataTable')    defineOp = op('define')    # 重置触发器    dataUpdateOp.par.resetpulse.pulse()    # 重置小蛇前进方向    controlConsOp.par.value1 = 0    # 重置小蛇身体数据    snakeDataTableOp.clear(keepFirstRow = True)    initSnakeDataRow = defineOp['gameGridRow'][0] // 2 + 1    initSnakeDataCol = defineOp['gameGridCol'][0] // 2 - 2      snakeDataTableOp.appendRow([initSnakeDataRow, initSnakeDataCol - 1, defineOp['snakeColorR'][0], defineOp['snakeColorG'][0], defineOp['snakeColorB'][0], 0.4])    snakeDataTableOp.appendRow([initSnakeDataRow, initSnakeDataCol, defineOp['snakeColorR'][0], defineOp['snakeColorG'][0], defineOp['snakeColorB'][0], 1])    # 重置得分点数据    controlConsOp.par.value2 = op('define')['gameGridRow'][0] // 2 + 1    controlConsOp.par.value3 = op('define')['gameGridCol'][0] // 2 + 3  return  def onCook(scriptOp):  # 定义输入OP  for OP in scriptOp.inputs:    if OP.name == 'dataUpdate':      dataUpdateOp = OP    elif OP.name == 'gridData':      gridDataOp = OP    elif OP.name == 'control':      controlOp = OP    elif OP.name == 'define':      defineOp = OP    elif OP.name == 'snakeData':      snakeDataOp = OP    elif OP.name == 'endAnimation':      endAnimationOp = OP  try:    dataUpdateOp    gridDataOp    controlOp    defineOp    snakeDataOp    endAnimationOp  except NameError:    scriptOp.clear()    return  # 定义各种参数  updateSignal = dataUpdateOp['dataUpdate'][0]  gameState = int(controlOp['gameState'][0])  snakeDiraction = int(controlOp['snakeDiraction'][0])  scorePointRow = int(controlOp['scorePointRow'][0])  scorePointCol = int(controlOp['scorePointCol'][0])  maxRow = int(defineOp['gameGridRow'][0])  maxCol = int(defineOp['gameGridCol'][0])   snakeColorR = int(defineOp['snakeColorR'][0])  snakeColorG = int(defineOp['snakeColorG'][0])  snakeColorB = int(defineOp['snakeColorB'][0])  scorePointColorR = int(defineOp['scorePointColorR'][0])  scorePointColorG = int(defineOp['scorePointColorG'][0])  scorePointColorB = int(defineOp['scorePointColorB'][0])  scorePointIndex = (scorePointRow - 1) * maxCol + scorePointCol - 1  # 待机状态  if gameState == 0 :    # 计算小蛇身体坐标位置    snakeDataIndex = []    for i in range(snakeDataOp.numSamples):      index = int((snakeDataOp['row'][i] - 1) * maxCol + snakeDataOp['col'][i] - 1)      snakeDataIndex.append(index)    # 输出数据    scriptOp.clear()    scaleChan = scriptOp.appendChan('scale')    rChan = scriptOp.appendChan('r')    gChan = scriptOp.appendChan('g')    bChan = scriptOp.appendChan('b')    aChan = scriptOp.appendChan('a')    scriptOp.numSamples = gridDataOp.numSamples    scaleChan[scorePointIndex] = 1    rChan[scorePointIndex] = scorePointColorR    gChan[scorePointIndex] = scorePointColorG    bChan[scorePointIndex] = scorePointColorB    aChan[scorePointIndex] = 1    for i, val in enumerate(snakeDataIndex):      if i == len(snakeDataIndex) - 1:        scaleChan[val] = 1      else:        scaleChan[val] = 0.9      rChan[val] = snakeDataOp['r'][i]      gChan[val] = snakeDataOp['g'][i]      bChan[val] = snakeDataOp['b'][i]      aChan[val] = snakeDataOp['a'][i]      return  # 游戏中状态  elif gameState == 1:    if updateSignal > 0.5:      # 定义参数      snakeData = []      numSnakeData = snakeDataOp.numSamples      snakeDataTableOp = op('snakeDataTable')      controlConsOp = op('controlCons')      # 获取旧的小蛇身体数据      for i in range(numSnakeData):        snakeData.append([snakeDataOp['row'][i], snakeDataOp['col'][i]])      # 根据前进方向计算小蛇新的头部位置      snakeHeadData = snakeData[numSnakeData - 1]      if snakeDiraction == 0:        newSnakeHeadDataCol = snakeHeadData[1] + 1        if newSnakeHeadDataCol > maxCol:          newSnakeHeadDataCol = 1        newSnakeHeadData = [snakeHeadData[0], newSnakeHeadDataCol]      elif snakeDiraction == 1:        newSnakeHeadDataRow = snakeHeadData[0] + 1        if newSnakeHeadDataRow > maxRow:          newSnakeHeadDataRow = 1        newSnakeHeadData = [newSnakeHeadDataRow, snakeHeadData[1]]      elif snakeDiraction == 2:        newSnakeHeadDataCol = snakeHeadData[1] - 1        if newSnakeHeadDataCol < 1:          newSnakeHeadDataCol = maxCol        newSnakeHeadData = [snakeHeadData[0], newSnakeHeadDataCol]      elif snakeDiraction == 3:        newSnakeHeadDataRow = snakeHeadData[0] - 1        if newSnakeHeadDataRow < 1:          newSnakeHeadDataRow = maxRow        newSnakeHeadData = [newSnakeHeadDataRow, snakeHeadData[1]]      # 判断小蛇新的头部位置有没有吃到得分点      if newSnakeHeadData[0] == scorePointRow and newSnakeHeadData[1] == scorePointCol:        # 将小蛇新的头部位置加入小蛇的数据集合中        snakeData.append(newSnakeHeadData)        # 如果小蛇身体长度达到最大,结束游戏        if len(snakeData) == gridDataOp.numSamples:          controlConsOp.par.value0 = 2          op('timer1').par.start.pulse()          return        # 重置得分点位置        while True:          mRandomRow = random.randint(1, maxRow)          mRandomCol = random.randint(1, maxCol)          mCheck = True          for i in range(len(snakeData)):            if mRandomRow == snakeData[i][0] and mRandomCol == snakeData[i][1]:              mCheck = False              break          if mCheck:            controlConsOp.par.value2 = mRandomRow            controlConsOp.par.value3 = mRandomCol            break      # 没有吃到得分点的情况      else:        # 将小蛇尾部的位置数据从小蛇的数据集合中删除        snakeData.pop(0)        # 判断小蛇新的头部数据是否碰撞到身体,如是则结束游戏        for s in snakeData:          if s[0] == newSnakeHeadData[0] and s[1] == newSnakeHeadData[1]:            controlConsOp.par.value0 = 2            op('timer1').par.start.pulse()            return        # 没有碰撞到身体,将小蛇新的头部位置加入小蛇的数据集合中        snakeData.append(newSnakeHeadData)      # 更新小蛇身体数据      snakeDataTableOp.clear(keepFirstRow = True)      snakeDataIndex = []      snakeAlphaData = []         for i, data in enumerate(snakeData):        alpha = 0.4 + (0.6 / (len(snakeData) - 1)) * i        snakeDataIndex.append(int((data[0] - 1) * maxCol + data[1] - 1))        snakeAlphaData.append(alpha)        snakeDataTableOp.appendRow([data[0], data[1], snakeColorR, snakeColorG, snakeColorB, alpha])      # 输出数据      scriptOp.clear()      scaleChan = scriptOp.appendChan('scale')      rChan = scriptOp.appendChan('r')      gChan = scriptOp.appendChan('g')      bChan = scriptOp.appendChan('b')      aChan = scriptOp.appendChan('a')      scriptOp.numSamples = gridDataOp.numSamples      scaleChan[scorePointIndex] = 1      rChan[scorePointIndex] = scorePointColorR      gChan[scorePointIndex] = scorePointColorG      bChan[scorePointIndex] = scorePointColorB      aChan[scorePointIndex] = 1      for i, val in enumerate(snakeDataIndex):        if i == len(snakeDataIndex) - 1:          scaleChan[val] = 1        else:          scaleChan[val] = 0.9        rChan[val] = snakeColorR        gChan[val] = snakeColorG        bChan[val] = snakeColorB        aChan[val] = snakeAlphaData[i]      return  # 游戏结束状态  elif gameState == 2:    # 计算小蛇身体坐标位置    snakeDataIndex = []    for i in range(snakeDataOp.numSamples):      index = int((snakeDataOp['row'][i] - 1) * maxCol + snakeDataOp['col'][i] - 1)      snakeDataIndex.append(index)    # 输出数据    scriptOp.clear()    scaleChan = scriptOp.appendChan('scale')    rChan = scriptOp.appendChan('r')    gChan = scriptOp.appendChan('g')    bChan = scriptOp.appendChan('b')    aChan = scriptOp.appendChan('a')    scriptOp.numSamples = gridDataOp.numSamples    scaleChan[scorePointIndex] = 1    rChan[scorePointIndex] = scorePointColorR    gChan[scorePointIndex] = scorePointColorG    bChan[scorePointIndex] = scorePointColorB    aChan[scorePointIndex] = 1    for i, val in enumerate(snakeDataIndex):      if i == len(snakeDataIndex) - 1:        scaleChan[val] = 1      else:        scaleChan[val] = 0.9      rChan[val] = snakeDataOp['r'][i]      gChan[val] = snakeDataOp['g'][i]      bChan[val] = snakeDataOp['b'][i]      aChan[val] = snakeDataOp['a'][i]      for i in range(int(endAnimationOp[0][0] * maxCol)):      scaleChan[i] = 0.9      rChan[i] = snakeColorR      gChan[i] = snakeColorG      bChan[i] = snakeColorB      aChan[i] = 1    return  # 未知状态,清空窗口  else:    scriptOp.clear()  return
ScriptCHOP的脚本编写完之后,贪吃蛇游戏就已经完成一大半啦。还剩下最后一个重要内容,就是游戏输入控制的脚本了。 在这个贪吃蛇程序中我使用了键盘的方向键作为游戏按键。获取键盘的方向键是否按下的事件我们可以使用KeyboardinDAT。

ffbd0305a83072b2cbe96149ee40de5c.png

keyboardinDAT

# 根据输入的头部与颈部位置判断当前蛇头所朝的方向def diraction(head, neck):  maxRow = op('define')['gameGridRow'][0]  maxCol = op('define')['gameGridCol'][0]  if head[0] == neck[0]:    if head[1] > neck[1]:      if head[1] == maxCol and neck[1] == 1:        return 2      else:        return 0    else:      if head[1] == 1 and neck[1] == maxCol:        return 0      else:        return 2  if head[1] == neck[1]:    if head[0] > neck[0]:      if head[1] == maxRow and neck[0] == 1:        return 3      else:        return 1    else:      if head[1] == 1 and neck[1] == maxRow:        return 1      else:        return 3def onKey(dat, key, character, alt, lAlt, rAlt, ctrl, lCtrl, rCtrl, shift, lShift, rShift, state, time, cmd, lCmd, rCmd):  gameState = int(op('control')['gameState'][0])  controlConsOp = op('controlCons')  # 游戏中状态  if gameState == 1:    # 按键被按下时    if state == 1:      # 定义参数      snakeDataOp = op('snakeData')      snakeDataOpSamples = snakeDataOp.numSamples      snakeHeadData = [snakeDataOp['row'][snakeDataOpSamples - 1], snakeDataOp['col'][snakeDataOpSamples - 1]]      snakeNeckData = [snakeDataOp['row'][snakeDataOpSamples - 2], snakeDataOp['col'][snakeDataOpSamples - 2]]      # 计算小蛇方向      snakeDiraction = diraction(snakeHeadData, snakeNeckData)      # 根据小蛇当前方向与键盘按键确定新方向      if snakeDiraction == 1 or snakeDiraction == 3:        if key == 'left':          controlConsOp.par.value1 = 2        elif key == 'right':          controlConsOp.par.value1 = 0      elif snakeDiraction == 0 or snakeDiraction == 2:        if key == 'up':          controlConsOp.par.value1 = 1        elif key == 'down':          controlConsOp.par.value1 = 3  # 游戏待机状态  elif gameState == 0:    # 按下按键开始游戏    if state == 1:      if key == 'up':        controlConsOp.par.value1 = 1      elif key == 'down':        controlConsOp.par.value1 = 3      controlConsOp.par.value0 = 1  return
到此,贪吃蛇游戏主体已经制作得差不多了。 - 结语 - 公众号后台回复”Snake“获取TD文件下载地址。 都看到这里了,不点个在看支持一下这个快要秃顶的小编吗? 4d2106f6086033e7b52ab631c314ec5d.png 4d2106f6086033e7b52ab631c314ec5d.png 4d2106f6086033e7b52ab631c314ec5d.png 原创不易 你们的关注是我们的最大动力 Follow f6deee158c68afb19a0464da67dc7af5.png

关  注  我  们

#

学习交流分享,请多多指教

Touchdesigner · Notch · Arduino · Blender

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值