代码之家  ›  专栏  ›  技术社区  ›  Mshnik

当其他精灵旋转时,Pixi.js精灵“抖动”

  •  3
  • Mshnik  · 技术社区  · 10 年前

    我正在Coffeescapet中开发一个Pixi.js游戏,主要关注旋转六边形。在设置旋转动画和识别旋转的基础板状态方面,关于旋转的一切都正常工作。然而,对于棋盘上的某些精灵(并非所有),旋转它们会导致舞台上的其他精灵(并非全部)“抖动”。他们暂时移到一边,然后在旋转结束时再次移回来。我一直在努力寻找这个问题;我发现的一个结果是,有人在不同的网站上有类似的问题,但没有答案: here .

    我不知道从哪里开始自己诊断这个bug。如果这是画布的问题,我可以在那里查看,但也可能是Pixi的问题。这是这个问题的gif图。当我单击第一个十六进制(右侧)时,许多行都会抖动。当我单击第二个十六进制(左侧)时,它们不会。

    就代码而言,该项目目前规模相当大。下面是动画函数和助手方法:

    ## The frame count the window is on ##
    @count = 0
    
    ### Moves the connector to the correct lit layer ###
    @toLit = (connector) ->
      try
        @colorContainers[connector.color].unlit.removeChild(connector.panel)
      catch
      if @typeIsArray connector.color
        if connector.color.length > 0
          c = connector.color[0].toUpperCase()
        else
          c = Color.asString(Color.NONE).toUpperCase()
      else
        c = connector.color.toUpperCase()
      if c is Color.asString(Color.NONE).toUpperCase()
        @toUnlit(connector) ## Prevent lighting of color.NONE
      else
        @colorContainers[c.toUpperCase()].lit.addChild(connector.panel)
        connector.linked = true
      return
    
    ### Moves the connector to the correct unlit layer ###
    @toUnlit = (connector) ->
      try
        @colorContainers[connector.color].lit.removeChild(connector.panel)
      catch
      if @typeIsArray connector.color
        if connector.color.length > 0
          c = connector.color[0]
        else
          c = Color.asString(Color.NONE)
      else
        c = connector.color
      if connector.hex? and connector.hex instanceof Crystal  
        @colorContainers[Color.asString(Color.NONE).toUpperCase()].unlit.addChild(connector.panel)
      else
        @colorContainers[c.toUpperCase()].unlit.addChild(connector.panel)
      connector.linked = false
      return
    
    ### Creates a frame offset for the each color ###
    @colorOffset = {}
    for c in Color.values()
      if not isNaN(c)
        c = Color.fromString(c).toUpperCase()
      else
        c = c.toUpperCase()
      @colorOffset[c] = Math.random() + 0.5
    
    ### Updates the pulse filter that controls lighting effects ###
    @calcPulseFilter = (count) ->
      for col, val of @colorContainers
        pulse = val.lit.filters[0]
        cont = (count + val.lit.pulseOffset)/val.lit.pulseLength
        m = pulse.matrix
        m[0] = Math.abs(Math.sin(cont * 2 * Math.PI)) * 0.5 + 0.5
        m[5] = Math.abs(Math.sin(cont * 2 * Math.PI)) * 0.5 + 0.5
        m[10] = Math.abs(Math.sin(cont * 2 * Math.PI)) * 0.5 + 0.5
        m[15] = Math.abs(Math.sin(cont * 2 * Math.PI)) * 0.25 + 0.75
        pulse.matrix = m
      for cont in @goalContainer.children
        if cont.children.length >= 2 and cont.filters.length >= 2
          pulse = cont.filters[1]
          correspondCont = @colorContainers[cont.children[0].color.toUpperCase()].lit
          c = (count + correspondCont.pulseOffset)/correspondCont.pulseLength
          m = pulse.matrix
          if parseInt(cont.children[1].text.substring(0, 1)) >= parseInt(cont.children[1].text.substring(2))
            m[0] = Math.abs(Math.sin(c * 2 * Math.PI)) * 0.5 + 0.5
            m[5] = Math.abs(Math.sin(c * 2 * Math.PI)) * 0.5 + 0.5
            m[10] = Math.abs(Math.sin(c * 2 * Math.PI)) * 0.5 + 0.5
            m[15] = Math.abs(Math.sin(c * 2 * Math.PI)) * 0.25 + 0.75
          else
            m[0] = 1
            m[5] = 1
            m[10] = 1
            m[15] = 1
          pulse.matrix = m
      return
    
    ### The animation function. Called by pixi and requests to be recalled ###
    @animate = () ->
        ## Color animation
        window.count += 1;  ## Frame count
        @calcPulseFilter(window.count)
        rotSpeed = 1/5
        tolerance = 0.000001 ## For floating point errors - difference below this is considered 'equal'
        radTo60Degree = 1.04719755 ## 1 radian * this coefficient = 60 degrees
        if (@BOARD?)
          ## Update text on goal
          curLit = @BOARD.crystalLitCount()
          goalContainer = @menu.children[@goalContainerIndex]
          isWin = true ## True if this user has won - every goal set.
          for pan in goalContainer.children
            for spr in pan.children
              if spr instanceof PIXI.Text and spr.color.toUpperCase() of curLit
                spr.setText(curLit[spr.color.toUpperCase()] + spr.text.substring(1))
                if curLit[spr.color.toUpperCase()] < parseInt(spr.text.substring(2))
                  isWin = false
    
          if isWin and (not @winContainer?) and @showWinContainer
            @gameOn = false
            @makeWinGameContainer()
    
          for h in @BOARD.allHexes()
            ##Update lighting of all hexes
            if h.isLit().length > 0 and not h.backPanel.children[0].lit
              h.backPanel.children[0].lit = true
              if not (h instanceof Prism)
                @toLit(h.backPanel.spr)
            if h.isLit().length is 0 and h.backPanel.children[0].lit
              h.backPanel.children[0].lit = false
              if not (h instanceof Prism)
                @toUnlit(h.backPanel.spr)
    
            hLit = h.isLit()
            if h instanceof Prism
              ## Adjust opacity of cores
              for col, core of h.cores
                if col.toLowerCase() not in hLit and core.alpha > 0
                  core.alpha = 0
                else if col.toLowerCase() in hLit and core.alpha is 0
                  core.alpha = 0.75
    
            nS = h.getNeighborsWithBlanks()
            ## Fixing lighting of connectors 
            for panel in h.colorPanels
              col = panel.color.toLowerCase()
              for connector in panel.children
                for side in connector.sides
                  n = nS[side]
    
                  if n? and col in hLit and n.colorOfSide(n.indexLinked(h)) is col and not connector.linked
                    @toLit(connector)
                    for nPanel in n.colorPanels
                      for nConnector in nPanel.children
                        for nSide in nConnector.sides
                          if nSide is n.indexLinked(h) and not nConnector.linked
                            @toLit(nConnector)
                  else if connector.linked and col not in hLit
                    @toUnlit(connector)
                    if n?
                      for nPanel in n.colorPanels
                        for nConnector in nPanel.children
                          for nSide in nConnector.sides
                            if nSide is n.indexLinked(h) and not nConnector.linked
                              @toUnlit(nConnector)
    
            ### Rotation of a prism - finds a prism that wants to rotate and rotates it a bit. ###
            ### If this is the first notification that this prism wants to rotate, stops providing light. ###
            ### If the prism is now done rotating, starts providing light again ###
            if h instanceof Prism and h.currentRotation isnt h.targetRotation
              if h.canLight
                h.canLight = false
                h.light()
              inc = 
                if (h.targetRotation - h.prevRotation) >= 0 
                  rotSpeed
                else
                  -rotSpeed
              h.backPanel.rotation += inc * radTo60Degree
              h.currentRotation += inc 
              for value in h.colorPanels
                value.rotation += inc * radTo60Degree
              if Math.abs(h.targetRotation - h.currentRotation) < tolerance
                inc = (h.targetRotation - h.currentRotation)
                h.backPanel.rotation += inc * radTo60Degree
                h.currentRotation += inc
                for value in h.colorPanels
                  value.rotation += inc * radTo60Degree
                  ## Update side index of each sprite
                  for spr in value.children
                    newSides = []
                    for side in spr.sides
                      newSides.push((side + (h.currentRotation - h.prevRotation)) %% Hex.SIDES)
                    spr.sides = newSides
                h.prevRotation = h.currentRotation
                h.canLight = true
                h.light()
    
            ### Spark and crystal color changing ###
            if (h instanceof Spark or h instanceof Crystal) and h.toColor isnt ""
              col = if (not isNaN(h.toColor)) 
                      Color.asString(h.toColor).toUpperCase() 
                    else 
                      h.toColor.toUpperCase()
              h.backPanel.spr.color = col
              @toLit(h.backPanel.spr)
              h.toColor = ""
        requestAnimFrame(animate )
        @renderer.render(@stage)
        return
    

    如果您认为代码中有任何其他部分与bug相关,请告诉我,我会将这些部分放上去。任何帮助或建议都将非常棒!

    1 回复  |  直到 10 年前
        1
  •  6
  •   Mshnik    10 年前

    突然想到了解决方案-当您旋转当前位于其父级DisplayObjectContainer()边界的精灵时,它会导致父级容器调整大小。它在整个旋转过程中继续调整大小,因为定义角的精灵正在移动。容器尺寸的变化会导致其相对于同级容器发生移动。

    修复方法是将一个不可见的静态精灵添加到容器中,在容器中旋转的对象比容器稍大。这样,您就可以保证旋转的精灵不是边缘之一,因此不会导致容器调整大小。