交通模拟器 汽车的行驶以及等红绿灯的场景进行模拟,使用了pixi和webgl去实现的。着色器的编写。
预览 github预览地址 gitee预览地址 github项目地址 
界面 
功能 
可以看到当前的帧率 
暂停按钮可以让动画暂停,再次点击即可继续动画 
 
👈🏻左侧区域:原始模式、多彩模式、多彩闪光模式、简笔画模式。  
多彩模式是可以让原始的汽车颜色进行更换多种颜色的更换多彩闪光模式是让汽车颜色一直随机变简笔画模式是让汽车是简笔画的形态pixi的sprite用的webgl来写了一部分sharder完成的。 
🚗车辆管理区域:禁用可以让该类型的车辆不在道路上行驶,启用则是相反,允许该车辆行驶。
技术实现 首先车辆的行驶方向是上下左右四个方向,这四个方向的车辆我这里是采用了链表的数据结构。数组也能实现。由于车辆要不断地删除添加的操作所以链表的效率会更高一些。这个项目中用到的链表也挺不难,就是链表的添加和删除,会这两个就能进行车辆的添加和删除。
车辆添加 车辆是pixijs的sprite,每种类型的车辆都是分为上下左右四张图片。
添加车辆就是在链表的最后添加上sprite,存储数据是 carData,它是个useRef。是分为left right top bottom四个字段,代表四个方向,每个字段都是一个链表,是每个方向的车辆。根据每个方向要对sprite 进行x,y点进行初始化,根据前一个链表节点的x和y,计算添加车辆的x和y。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 if  (carData.current [direction]) {  let  current = carData.current [direction]!;      while  (current.next ) {     current = current.next !;   }      switch  (direction) {     case  'left' :       sprite.y  = (HEIGHT  - ROADWIDTH ) / 2  + ROADWIDTH  / 4 ;       if  (current.val .x  >= WIDTH  - CARLENGTH  / 2 ) {         sprite.x  = current.val .x  + CARLENGTH  * 1.5 ;       } else  {         sprite.x  = WIDTH  - CARLENGTH  / 2 ;       }       break ;     case  'right' :       sprite.y  = HEIGHT  / 2  + ROADWIDTH  / 4 ;       if  (current.val .x  <= -CARLENGTH  / 2 ) {         sprite.x  = current.val .x  - CARLENGTH  * 1.5 ;       } else  {         sprite.x  = -CARLENGTH  / 2 ;       }       break ;     case  'top' :       sprite.x  = WIDTH  / 2  + ROADWIDTH  / 4 ;       if  (current.val .y  >= HEIGHT  + CARLENGTH  / 2 ) {         sprite.y  = current.val .y  + CARLENGTH  * 1.5 ;       } else  {         sprite.y  = HEIGHT  + CARLENGTH  / 2 ;       }       break ;     case  'bottom' :       sprite.x  = WIDTH  / 2  - ROADWIDTH  / 4 ;       if  (current.val .y  <= -CARLENGTH  / 2 ) {         sprite.y  = current.val .y  - CARLENGTH  * 1.5 ;       } else  {         sprite.y  = -CARLENGTH  / 2 ;       }       break ;   }   current.next  = new  ListNode (sprite); } else  {   if  (direction === 'left' ) {      sprite.y  = (HEIGHT  - ROADWIDTH ) / 2  + ROADWIDTH  / 4 ;     sprite.x  = WIDTH  - CARLENGTH  / 2 ;   } else  if  (direction === 'right' ) {      sprite.y  = HEIGHT  / 2  + ROADWIDTH  / 4 ;     sprite.x  = -CARLENGTH  / 2 ;   } else  if  (direction === 'top' ) {       sprite.x  = WIDTH  / 2  + ROADWIDTH  / 4 ;     sprite.y  = HEIGHT  + CARLENGTH  / 2 ;   } else  if  (direction === 'bottom' ) {       sprite.x  = WIDTH  / 2  - ROADWIDTH  / 4 ;     sprite.y  = -CARLENGTH  / 2 ;   }   carData.current [direction] = new  ListNode (sprite); } 
车辆行驶 车辆行驶需要考虑几点:
当前面红灯怎么办? 
后面的车辆要撞到前一个车辆怎么办? 
为了灵活性,汽车什么时候加速,什么时候减速? 
 
下面一一解答:
当为红灯的时候,下一步就要闯红灯了,这时需要让车辆停止,位置一直保持为原来的位置 
后面的车要撞到前面的车,有两种情况:一种是前面的车在等红绿灯,另一种是都是行驶状态,后面车的车速大于前面车的车速。这时候需要设置一个阈值 min,当后车与前车的距离小于这个min的时候,需要让后车相对于前车速度进行减速,如果减速还是会小于这个min的话,说明前车在等红绿灯,这时候后车的位置等于这个min就行了,保证不小于这个min。 
上面说设置一个车距之间的最小阈值 min,是为了让车减速,加速则是要设置车距之间的最大阈值 max,车距超过这个max就进行加速操作。 
 
车辆模式 因为pixijs默认是使用webgl去进行渲染的,sprite是支持使用webgl编写着色器的。根据选择的不同模式进行着色器sharder的编写完成的。  
多彩模式:生成一个随机的rgb三个通道的值,然后到通过uniform传入到片元着色器与当前的色值进行相乘,深色部分打算是保留下来的,所以设置一个阈值,超过这个阈值代表浅色,浅色部分会去跟随机rgb进行相乘。
多彩闪光模式:和多彩模式实现是一样的,区别是动画每帧都会使用多彩,所以有了多彩闪光的一个效果。
简笔画模式:这个是我写多彩模式的sharder无意间实现出来的,原理也是很简单的,将浅色部分都变成白色,只留下深色部分,就是简笔画的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 export  const  carFilterColor  = (sprite: Sprite, type?: number ) => {  const  fragStr = `      varying vec2 vTextureCoord;     uniform sampler2D uSampler;     uniform vec2 size;     uniform vec3 secondaryColor;     uniform float type;          void main(void){       vec2 uv = size;       vec4 color = texture2D(uSampler, vTextureCoord);       // 阈值       float num = 0.3;       if(type != 0.0){         if(color.r > num || color.g  >= num || color.b >= num){             if(type == 1.0 || type == 2.0 ){ / 多彩或多彩闪光模式                 color.rgb *= secondaryColor;             }else if (type == 3.0){  // 简笔画模式                 color.rgb = vec3(1.0);             }         }         color.rgb = clamp(vec3(0.0),color.rgb,vec3(1.0));       }       gl_FragColor = color;     }   ` ;  let  filter = new  PIXI .Filter (undefined , fragStr, {     secondaryColor : [       Math .random () + 0.6 ,       Math .random () + 0.6 ,       Math .random () + 0.6 ,     ],     type : type || 0 ,   });   sprite.filters  = [filter]; }; 
还有一些细节点就不一一赘述了,如:交通灯的设计和控制、车辆类型的控制等等。