当前位置:嗨网首页>书籍在线阅读

34-第四次迭代_重用预加载的声音

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

7.6.7 第四次迭代:重用预加载的声音

尽管第三次迭代中的代码已经非常整洁,但并不奏效。需要用户做一些妥协,实现一个不那么优雅,但是几乎能够在每个需要的时刻播放声音的方案。无论是从本地加载还是从远程网站加载,这方案都应该有效。

在最后一次迭代中,将使用一个类似第三次迭代中的声音池,只是操作方法不同。这里不重用不同声音文件的声音对象。相反,事先加载所有需要的声音,仅当声音对象没有被使用时才播放它。在实际中,分别为两个声音各创建 3 个声音对象。这样,在启动程序时一共创建了 6 个声音对象。虽然看上去不是一个完美的方案,但在所有的浏览器中都运转得相当好,而且播放声音的效率也最高。

在canvasApp()函数中,将变量MAX_SOUNDS设置为6。也可以设置得更高,但在本例中,将它作为创建以及预加载的声音数量的限制。

var MAX_SOUNDS = 6;

然后创建6个变量,保存HTMLAudioElement对象,其中3个是爆炸声。

var explodeSound ;
var explodeSound2 ;
var explodeSound3 ;

另外,还有3个是射击声。

var shootSound;
var shootSound2;
var shootSound3;

在initApp()中预加载所有的声音对象。要多次加载相同的对象。

explodeSound = document.createElement("audio");
document.body.appendChild(explodeSound);
audioType = supportedAudioFormat(explodeSound);
explodeSound.addEventListener("canplaythrough",itemLoaded,false);
explodeSound.setAttribute("src", "explode1." + audioType);
explodeSound2 = document.createElement("audio");
document.body.appendChild(explodeSound2);
explodeSound2.addEventListener("canplaythrough",itemLoaded,false);
explodeSound2.setAttribute("src", "explode1." + audioType);
explodeSound3 = document.createElement("audio");
document.body.appendChild(explodeSound3);
explodeSound3.addEventListener("canplaythrough",itemLoaded,false);
explodeSound3.setAttribute("src", "explode1." + audioType);
shootSound = document.createElement("audio");
document.body.appendChild(shootSound);
shootSound.addEventListener("canplaythrough",itemLoaded,false);
shootSound.setAttribute("src", "shoot1." + audioType);
shootSound2 = document.createElement("audio");
document.body.appendChild(shootSound2);
shootSound2.addEventListener("canplaythrough",itemLoaded,false);
shootSound2.setAttribute("src", "shoot1." + audioType);
shootSound3 = document.createElement("audio");
document.body.appendChild(shootSound3);
shootSound3.addEventListener("canplaythrough",itemLoaded,false);
shootSound3.setAttribute("src", "shoot1." + audioType);

在itemLoaded()函数中,为6个已加载的声音移除事件监听器。

shootSound.removeEventListener("canplaythrough",itemLoaded, false);
shootSound2.removeEventListener("canplaythrough",itemLoaded, false);
shootSound3.removeEventListener("canplaythrough",itemLoaded, false);
explodeSound.removeEventListener("canplaythrough",itemLoaded,false);
explodeSound2.removeEventListener("canplaythrough",itemLoaded,false);
explodeSound3.removeEventListener("canplaythrough",itemLoaded,false);

然后,将每个声音加入soundPool数组。然而,本次将它们作为动态对象加入数组,这样就可以设置以下HTMLAudioElement对象中不存在的属性。

  • name:要播放的声音文件的名字(不包含扩展名)。
  • element:HTMLAudioElement对象的引用。
  • played:布尔值,指示该声音是否曾被播放。之所以需要这个属性,是因为要把所有声音对象都放入数组中,但是它们还未曾播放。这意味着它们的ended属性还没有被设置为true。played属性可以指示声音是否已经准备播放,即未曾播放。在声音播放一次之后,将该属性设置为true。
soundPool.push({name:"explode1", element:explodeSound, played:false});
soundPool.push({name:"explode1", element:explodeSound2, played:false});
soundPool.push({name:"explode1", element:explodeSound3, played:false});
soundPool.push({name:"shoot1", element:shootSound, played:false});
soundPool.push({name:"shoot1", element:shootSound2, played:false});
soundPool.push({name:"shoot1", element:shootSound3, played:false});

现在需要修改resetApp()函数。这个修改将支持在Chrome中播放声音。在这种工作方式下,Chrome是唯一一个加载声音时有小问题的浏览器。在Chrome中第一次播放声音时,会在开始前有一个停顿。为了改变这点,先播放每种声音一次。将音量设为0,保证在Chrome中第一次调用playSound()函数时,声音已经加载完毕并准备播放了。

function resetApp(){
  playSound(SOUND_EXPLODE,0);
  playSound(SOUND_SHOOT,0);
  startLevel();
  appState = STATE_PLAYING;
  }

playSound()函数的操作方式与第三次迭代类似,循环遍历soundPool数组查找可以播放的声音。然而在这个版本中,除了检查HTMLAudioElement是否播放完毕(tSound.element. ended)或者是否还未播放(!tSound.played),还要检查sound参数的值是否与soundPool中声音对象的name属性匹配(tSound.name == sound)。

function playSound(sound,volume){
   var soundFound = false;
   var soundIndex = 0;
   var tempSound;
   if (soundPool.length > 0){
     while (!soundFound && soundIndex < soundPool.length){
      var tSound = soundPool[soundIndex];
      if ((tSound.element.ended || !tSound.played)&& tSound.name == sound){
        soundFound = true;
        tSound.played = true;
      } else {
        soundIndex++;
      }
     }
   }

用这个方法,只有在需要播放的声音文件加载完毕之后,声音未曾播放或已播放完毕时才播放它。这样就没有加载前的停顿(大多数情况),并且可以在需要的时间精确地播放声音。如果需要更多的声音,可以提前加载,或者将MAX_SOUNDS的值设置为大于预加载声音的数量。如果这样做,就可以在运行时创建新的声音对象(尽管这样做会导致从网络服务器上加载声音时产生停顿)。

if (soundFound){
     tempSound = soundPool[soundIndex].element;
     tempSound.volume = volume;
     tempSound.play();
   } else if (soundPool.length < MAX_SOUNDS){
     tempSound = document.createElement("audio");
     tempSound.setAttribute("src", sound + "." + audioType);
     tempSound.volume = volume;
     tempSound.play();
     soundPool.push({name:sound, element:tempSound, type:audioType, played:true});
   }

读者可以打开示例代码中的CH7EX9.html文件运行程序。

1.改善游戏的其他技巧

由于接下来的两章将介绍游戏的概念,因此不需要继续讨论太空掠夺者了。如果读者想继续完善这个游戏,这里有一些可以考虑的方面。

(1)添加分数。

(2)在每一级别增加外星人的速度。

(3)外星人与玩家的碰撞检测。

(4)为外星人和导弹建立对象池。

(5)使用wait()语句或帧计数器降低发射速度。

(6)添加爆炸效果。

(7)包含标题场景、级别场景和游戏结束场景。

(8)添加一个循环音轨。

2.太空掠夺者游戏的最终代码

例A-1展示了太空掠夺者游戏的最终代码(CH7EX9.html)。