Ninja Slice!

import bgefrom mathutils import Vector
import mathutils
import random


def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner


    rot = own.scene.objects['Emit'].children[0]
    
    own['Fx']= [own.scene.objects['Emit']]
    
    if 'Grid' not in own:
        own['Grid']={}
        own['Used']={}
        
        
        mesh = own.meshes[0]
        size = mesh.getVertexArrayLength(0)
        kd = mathutils.kdtree.KDTree(size)
        
        for i in range(size):
            vert = own.meshes[0].getVertex(0,i)
            kd.insert(vert.XYZ, i)
            


        kd.balance()


        #get all planes and there offset from the grid offset point
        
        for x in range(128):
            pos = own.worldTransform*Vector([x*2.5,0,0])
            cube = kd.find_range(pos, 2.25)
            front = []
            back = []
            for returned in cube:
                vert = own.meshes[0].getVertex(0,returned[1])
                vert.color = [x*.5,0,0,0]
                
                local = returned[0]-pos 
                if returned[0].x<pos.x:
                    front.append([vert,local])
                else:
                    back.append([vert,local])   
            if len(cube)>0:    
                own['Grid'].update({(x):[pos,[front,back],30,1,'']})
     
     
                
    # every frame append 1 plane to fx for each agent        
    grab = []        
    if own['Timer']==0:
        for key in own['Grid']:
            
            if len(own['Fx'])>0:
                own['Timer']=0
                
                
                item = own['Grid'][key]
                item[4]=own['Fx'][0]
                #used later 
                if 'Last' not in own['Fx'][0]:
                    item[4]['Last']=item[4].worldTransform.copy()    
                    
                 
                    
                #Set intial position of plane   
                item[2]=32
                item[0]=own['Fx'][0].worldPosition.copy()
                #set pos of leading edge
                for vertex in item[1][1]:
                    vertex[0].XYZ = item[4].worldTransform*vertex[1]+item[4].worldOrientation.col[0]*-.05
                    vertex[0].color = [1,1,1,1]
                
                #set pos of following edge    
                for vertex in item[1][0]:
                    vertex[0].XYZ = item[4]['Last']*vertex[1]
                    vertex[0].color = [1,1,1,.95]    
                item[4]['Last']=item[4].worldTransform.copy()    
                
                grab.append(key)
                own['Fx'].pop(0)
                
            else:
                break    
    
    
    #transfer plane from free to used
    for key in grab:
        own['Used'].update({key:own['Grid'][key]})
        del own['Grid'][key]
        
            
            
    
    
    #run each plane until it's value is zero and pop it
    
    grab = []
    index=0
    for itemKey in own['Used']:
        item = own['Used'][itemKey]
        
        if item[2]>=1:
            
            item[2]-=1
            
            #Set vertex color fade
            for vertex in item[1][1]:
                
                
                vertex[0].color = (vertex[0].color*31+Vector([.005,.005,.005,.001]))/32
            for vertex in item[1][0]:
               
                
                vertex[0].color = (vertex[0].color*31+Vector([.005,.005,.005,.001]))/32    
        
        
        #ready to pop item from dict         
        else:
            item[0] = item[4].worldPosition.copy()
            item[2]=15
            item[3]=.95
            for vertex in item[1][0]:
                vertex[0].XYZ=[0,0,0]
                vertex[0].color = [0,0,0,0]
            for vertex in item[1][1]:
                vertex[0].color = [0,0,0,0]
                vertex[0].XYZ=[0,0,0]    
                
            grab.append(itemKey)
            
        
    #Pop is here    
    for key in grab:
        own['Grid'].update({key:own['Used'][key]})
        del own['Used'][key]    
        #now it's ready to be used again    
    
    
    
    #run timer
    if own['Timer']>=1:
        own['Timer']-=1        
    
            
    
    #own['Last']=own.worldTransform.copy()
      
                
main()



Much honor and slice for the people.
many of the cutting.

Attachments

kdTree_grab_planeNinja_bladeFX_fixed_tiny_gap.blend (626 KB)

Hmm, interesting that you grab trail sections from the end and move them to the front. It means you don’t have to move all the vertices each frame. Clever.
I’ll have to have a think about this, and if I could improve the performance of my existing system by doing something similar. (Currently my trail system involves each vertex being moved to the position of the one before, and so once you get up past 2000 vertices you start noticing it occupying a fair bit of performance). Currently mine is a bit faster than yours at the 1000 poly level, but for very long trails yours will win out - and I there is a fair bit of potential for optimising this script…

That is so neat!
A few comments:
No need to import random
timer isn’t used
Number of trailing elements is ruled by the magic 128, because you pre-generated the geometry? Added an adjustment, though it seems to be wonky at high NUMPLANES and low END_OPACITY. Played around with it a bit. Seems like there’s a lot of unnecessary stuff going on in there, but that’s probably because I don’t understand it. Here’s my version with adjustable start and end opacity.


import bge
from mathutils import Vector
from mathutils import kdtree


START_OPACITY = .8
END_OPACITY = .5
NUMPLANES = 32


STARTCOLOR = Vector([1,1,1,START_OPACITY])
ENDCOLOR = Vector([0.005,0.005,0.005,END_OPACITY])
FADE = (STARTCOLOR - ENDCOLOR ) / NUMPLANES


def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner




    rot = own.scene.objects['Emit'].children[0]
    
    own['Fx']= [own.scene.objects['Emit']]
    
    if 'Grid' not in own:
        own['Grid']={}
        own['Used']={}
        
        mesh = own.meshes[0]
        size = mesh.getVertexArrayLength(0)
        kd = kdtree.KDTree(size)
        
        for i in range(size):
            vert = own.meshes[0].getVertex(0,i)
            kd.insert(vert.XYZ, i)
            
        kd.balance()


        #get all planes and their offset from the grid offset point
        
        for x in range(NUMPLANES):
            pos = own.worldTransform*Vector([x*2.5,0,0])
            cube = kd.find_range(pos, 2.25)
            front = []
            back = []
            for returned in cube:
                vert = own.meshes[0].getVertex(0,returned[1])
                vert.color = STARTCOLOR
                
                local = returned[0]-pos 
                if returned[0].x<pos.x:
                    front.append([vert,local])
                else:
                    back.append([vert,local])   
            if len(cube)>0:    
                own['Grid'].update({(x):[pos,[front,back],30,1,'']})
     
     
                
    # every frame append 1 plane to fx for each agent        
    grab = []
    for key in own['Grid']:
        
        if len(own['Fx'])>0:
            item = own['Grid'][key]
            item[4]=own['Fx'][0]
            #used later 
            if 'Last' not in own['Fx'][0]:
                item[4]['Last']=item[4].worldTransform.copy()    
                
             
                
            #Set intial position of plane   
            item[2]=NUMPLANES
            item[0]=own['Fx'][0].worldPosition.copy()
            #set pos of leading edge
            for vertex in item[1][1]:
                vertex[0].XYZ = item[4].worldTransform*vertex[1]+item[4].worldOrientation.col[0]*-.05
                vertex[0].color = STARTCOLOR
            
            #set pos of following edge    
            for vertex in item[1][0]:
                vertex[0].XYZ = item[4]['Last']*vertex[1]
                vertex[0].color = STARTCOLOR - FADE
            item[4]['Last']=item[4].worldTransform.copy()    
            
            grab.append(key)
            own['Fx'].pop(0)


    
    #transfer plane from free to used
    for key in grab:
        own['Used'].update({key:own['Grid'][key]})
        del own['Grid'][key]
        
    #run each plane until it's value is zero and pop it
    grab = []
    for itemKey in own['Used']:
        item = own['Used'][itemKey]
        
        if item[2]>0:
            
            item[2]-=1
            
            #Set vertex color fade
            for vertex in item[1][1]:
                vertex[0].color = vertex[0].color - FADE
                
            for vertex in item[1][0]:
                vertex[0].color = vertex[0].color - FADE
        
        
        #ready to pop item from dict         
        else:
            item[0] = item[4].worldPosition.copy()
            item[2]=15
            item[3]=.95
            for vertex in item[1][0]:
                vertex[0].XYZ=[0,0,0]
                vertex[0].color = [0,0,0,0]
            for vertex in item[1][1]:
                vertex[0].color = [0,0,0,0]
                vertex[0].XYZ=[0,0,0]    
                
            grab.append(itemKey)
            
        
    #Pop is here    
    for key in grab:
        own['Grid'].update({key:own['Used'][key]})
        del own['Used'][key]    
        #now it's ready to be used again
      
                
main()