最新公告
  • 欢迎您光临大资源网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!怎么把网页设置成桌面图标
  • HTML5全民飞机大战网页小游戏源码

    HTML5全民飞机大战网页小游戏源码 最后编辑:2026-06-05
    增值服务: 自动发货 使用说明 安装指导 环境配置二次开发BUG修复

    HTML5全民飞机大战网页小游戏源码,html+js实现飞机大战网页小游戏,玩法比较为简单,左右移动鼠标打击,不让前面的小飞机经过。飞机掉落速度越来越快,游戏结束后有分数显示,可以用来做分数比拼。

    1、html页面代码

    <!DOCTYPE html>
    <html lang=”en”>
    <head>
    <meta charset=”UTF-8″>
    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
    <meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>HTML5全民飞机大战小游戏 – bokequ.com</title>
    <style>
    html {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    position: relative;
    background-image: linear-gradient(#2C3E50,#4CA1AF);

    }

    .canvasbig {
    position: absolute;
    left: calc(50% – 260px);
    top: calc(50% – 400px);
    width: 520px;
    height: 800px;

    }

    .canvasdiv {
    position: absolute;
    cursor: pointer;
    left: 160px;
    top: 500px;
    width: 200px;
    height: 53px;
    background-image: url(img/starting.png);
    }

    .none {
    display: none;
    }
    </style>
    </head>
    <body>
    <!– <script src=”js/circle.js”></script> –>
    <div class=”canvasbig”>
    <div class=”canvasdiv”></div>
    <canvas width=”520″ height=”800″ id=”canvas1″></canvas>
    </div>
    <script src=”js/index.js”></script>
    </body>
    </html>

    2、js代码

    1. var canvas1 = document.getElementById(“canvas1”);
    2. var context = canvas1.getContext(‘2d’);
    3. var canvasbig = document.getElementsByClassName(“canvasbig”)[0];
    4. //背景图片
    5. var bg = new Image();
    6. bg.src = “img/bg.jpg”;
    7. //全民飞机大战标题
    8. var starthead = new Image();
    9. starthead.src = “img/starthead.png”;
    10. //加载时候的狗子和文字
    11. var load = new Image();
    12. var loadnum = 1,
    13. loadtime = 0,
    14. loadrect = 1;
    15. var loadtextblur = true,
    16. loadtextnum = 1,
    17. pointnum = 1;
    18. //我方战斗机
    19. var myplane = new Image();
    20. myplane.src = “img/myplane1.png”;
    21. var myplaneX = canvas1.width / 2,
    22. myplaneY = 730;
    23. //战斗机子弹
    24. var bullet = new Image();
    25. bullet.src = “img/bullet.png”;
    26. var bullettime = 0,
    27. bulletnum = 0,
    28. bulletarr = [];
    29. //敌机
    30. var enemytime = 0,
    31. enemyarr = [];
    32. var enemy1 = new Image();
    33. enemy1.src = `img/enemy1.png`;
    34. var enemy2 = new Image();
    35. enemy2.src = `img/enemy2.png`;
    36. var enemy3 = new Image();
    37. enemy3.src = `img/enemy3.png`;
    38. var enemy4 = new Image();
    39. enemy4.src = `img/enemy4.png`;
    40. var enemyall = [enemy1, enemy2, enemy3, enemy4];
    41. //战斗机爆炸
    42. var myplane1boom = new Image();
    43. var myboomnum = 1,
    44. myboomtime = 0;
    45. //敌机爆炸
    46. var enemychangearr = [];
    47. //boss警告
    48. var warning1 = new Image();
    49. warning1.src = “img/warning1.png”
    50. var warning2 = new Image();
    51. warning2.src = “img/warning2.png”;
    52. var warningtime = 0,
    53. warningchange = 0;
    54. //boss出场背景
    55. var bossbg = new Image();
    56. bossbg.src = “img/bg2.jpg”;
    57. var boss = new Image();
    58. boss.src = “img/planeboss.png”;
    59. //boss改变飞机速度
    60. var bossattacktime = 0;
    61. var bossattacknum = 1;
    62.  
    63. var obj = {
    64. gamestart: 1,
    65. gameload: 0,
    66. gamerun: 0,
    67. gameover: 0,
    68. dead: 0,
    69. score: 0,
    70. life: 3,
    71. bgy1: 854,
    72. bgy2: 0,
    73. warnon: 0,
    74. bosstime: 0,
    75. bossattack: 0,
    76. bgon: function () {
    77. context.drawImage(bg, 0, this.bgy1, 520, 854);
    78. context.drawImage(bg, 0, this.bgy2, 520, 854);
    79. },
    80. bgchange: function () {
    81. this.bgy1++;
    82. this.bgy2++;
    83. if (this.bgy1 == 0) {
    84. this.bgy1 = 854;
    85. this.bgy2 = 0;
    86. }
    87. },
    88. scoring: function () {
    89. var gradient = context.createLinearGradient(0, 0, 120, 60);
    90. gradient.addColorStop(0, ‘#ff9569’);
    91. gradient.addColorStop(1, ‘#e92758’);
    92. context.font = ’30px sans-serif’;
    93. context.fillStyle = gradient;
    94. context.fillText(“SCORE:” + this.score, 10, 50);
    95. },
    96. lifeing: function () {
    97. context.font = ’30px sans-serif’;
    98. context.fillStyle = “#D28140”;
    99. context.fillText(“LIFE:” + this.life, 400, 50);
    100. if (obj.dead == 1 && myboomnum == 9 && obj.life > 0) {
    101. obj.dead = 0;
    102. bullettime = 0;
    103. bulletnum = 0;
    104. bulletarr = [];
    105. enemytime = 0;
    106. enemyarr = [];
    107. myboomnum = 1;
    108. myboomtime = 0;
    109. enemychangearr = [];
    110. myplane1boom.src = `img/myplane1boom${myboomnum}.png`;
    111. } else if (obj.dead == 1 && obj.life == 0) {
    112. obj.gameover = 1;
    113. }
    114. },
    115. gameovering: function () {
    116. if (obj.gameover == 1) {
    117. obj.gamestart = 1;
    118. obj.gameover = 0;
    119. obj.dead = 0;
    120. obj.gamerun = 0;
    121. }
    122. },
    123. starting: function () {
    124. canvasdiv.className = “canvasdiv”;
    125. obj.life = 3;
    126. obj.score = 0;
    127. loadnum = 1;
    128. loadtime = 0;
    129. loadrect = 1;
    130. loadtextblur = true;
    131. loadtextnum = 1;
    132. pointnum = 1;
    133. myplaneX = canvas1.width / 2;
    134. myplaneY = 730;
    135. bullettime = 0;
    136. bulletnum = 0;
    137. bulletarr = [];
    138. enemytime = 0;
    139. enemyarr = [];
    140. myboomnum = 1;
    141. myboomtime = 0;
    142. enemychangearr = [];
    143. warningtime = 0;
    144. warningchange = 0;
    145. bossattacktime = 0;
    146. bossattacknum = 1;
    147. obj.bossbgy1 = 2420,
    148. obj.bossbgy2 = 1640,
    149. obj.bossbgy3 = 860,
    150. obj.bg2boss = 262,
    151. obj.bosstimeblur = true,
    152. obj.bossattack = 0;
    153. context.drawImage(starthead, 110, 200);
    154. },
    155. loading: function () {
    156. loadtime++;
    157. loadrect++;
    158. if (loadtime == 5) {
    159. loadtime = 0;
    160. loadnum++;
    161. if (loadnum == 10) {
    162. loadnum = 1;
    163. }
    164. load.src = `img/load${loadnum}.png`;
    165. }
    166. context.beginPath();
    167. context.fillStyle = ‘white’;
    168. context.fillRect(0, 0, 520, 800);
    169. var gradient = context.createLinearGradient(20, 500, 397, 30);
    170. gradient.addColorStop(0, ‘#29bdd9’);
    171. gradient.addColorStop(1, ‘#276ace’);
    172. context.fillStyle = gradient;
    173. context.fillRect(20, 500, loadrect, 30);
    174. context.closePath();
    175. context.drawImage(load, loadrect + 20, 480, 102, 72);
    176.  
    177. },
    178. loadtext: function () {
    179. if (loadtextblur == false) {
    180. loadtextnum–;
    181. } else if (loadtextblur == true) {
    182. loadtextnum += 2;
    183. }
    184. context.beginPath();
    185. context.font = ’40px sans-serif’;
    186. context.fillStyle = ‘black’;
    187. context.fillText(“加载中 “, 50, 450);
    188. context.beginPath();
    189. context.font = ’80px sans-serif’;
    190. if (pointnum == 1) {
    191. context.fillText(“. “, 200, 450 loadtextnum);
    192. context.fillText(“. “, 240, 450);
    193. context.fillText(“. “, 280, 450);
    194. if (loadtextnum < 0) {
    195. pointnum = 2;
    196. loadtextnum = 0;
    197. loadtextblur = true;
    198. } else if (loadtextnum > 39) {
    199. loadtextblur = false;
    200. }
    201. } else if (pointnum == 2) {
    202. context.fillText(“. “, 200, 450);
    203. context.fillText(“. “, 240, 450 loadtextnum);
    204. context.fillText(“. “, 280, 450);
    205. if (loadtextnum < 0) {
    206. pointnum = 3;
    207. loadtextnum = 0;
    208. loadtextblur = true;
    209. } else if (loadtextnum > 39) {
    210. loadtextblur = false;
    211. }
    212. } else if (pointnum == 3) {
    213. context.fillText(“. “, 200, 450);
    214. context.fillText(“. “, 240, 450);
    215. context.fillText(“. “, 280, 450 loadtextnum);
    216. if (loadtextnum < 0) {
    217. pointnum = 1;
    218. loadtextnum = 0;
    219. loadtextblur = true;
    220. } else if (loadtextnum > 39) {
    221. loadtextblur = false;
    222. }
    223. }
    224. context.closePath();
    225. },
    226. myplane: function (e) {
    227.  
    228. myplaneX = e.offsetX;
    229. myplaneY = e.offsetY;
    230. context.drawImage(myplane, myplaneX myplane.width / 2, myplaneY myplane.height / 2);
    231. },
    232. bulleton: function () {
    233. bullettime++;
    234. var bulletX = myplaneX bullet.width / 2;
    235. var bulletY = myplaneY myplane.height / 2 bullet.height;
    236. var num;
    237. if (obj.bossattack == 1) {
    238. num = 10
    239. }else{
    240. num = 20
    241. }
    242. if (bullettime >= num) {
    243. var changearr = [bulletX, bulletY, 0];
    244. bulletarr.push(changearr);
    245. bullettime = 0;
    246. }
    247. },
    248. bulletchange: function () {
    249. var result = [];
    250. for (var i = 0; i < bulletarr.length; i++) {
    251. if (bulletarr[i][1] bulletarr[i][2] >= 0) {
    252. context.drawImage(bullet, bulletarr[i][0], bulletarr[i][1] bulletarr[i][2]);
    253. bulletarr[i][2] += 4;
    254. result.push(bulletarr[i]);
    255. }
    256. }
    257. bulletarr = result;
    258. },
    259. enemy: function () {
    260. enemytime++;
    261. var enemynum = parseInt(Math.random() * 4);
    262. if (obj.bossattack == 1) {
    263. num = 10
    264. }else{
    265. num = 25
    266. }
    267. if (enemytime >= num) {
    268. if (enemynum == 3 && Math.random() < 0.9) {
    269. return;
    270. } else {
    271. var enemylife = 1
    272. if (enemynum == 3) {
    273. enemylife = 5
    274. }
    275. var changearr = [Math.random() * 520 enemyall[enemynum].width / 2, enemyall[enemynum].height, 0, enemynum, enemylife];
    276. enemyarr.push(changearr);
    277. enemytime = 0;
    278. }
    279. }
    280. },
    281. enemychange: function () {
    282. var result = [];
    283. if (obj.bossattack == 1) {
    284. bossattacktime++;
    285. if (bossattacktime == 80) {
    286. bossattacknum += 0.05;
    287. bossattacktime = 0;
    288. }
    289. }
    290. for (let i = 0; i < enemyarr.length; i++) {
    291. if (enemyarr[i][1] + enemyarr[i][2] <= canvas1.height) {
    292. context.drawImage(enemyall[enemyarr[i][3]], enemyarr[i][0], enemyarr[i][1] + enemyarr[i][2]);
    293. if (enemyall[enemyarr[i][3]] == enemy4) {
    294. enemyarr[i][2] += 1.5* bossattacknum;
    295. } else {
    296. enemyarr[i][2] += 2* bossattacknum;
    297. }
    298. result.push(enemyarr[i]);
    299. }
    300. }
    301. enemyarr = result;
    302. },
    303. myplaneboom: function () {
    304. obj.dead = 1;
    305. myboomtime++;
    306. if (myboomtime >= 10) {
    307. myplane1boom.src = `img/myplane1boom${myboomnum}.png`;
    308. myboomnum++;
    309. myboomtime = 0;
    310. }
    311. context.drawImage(myplane1boom, myplaneX myplane.width / 2, myplaneY myplane.height / 2);
    312. if (myboomnum == 9) {
    313. obj.life -= 1;
    314. bulletarr = [];
    315. enemyarr = [];
    316. myplaneX = canvas1.width / 2;
    317. myplaneY = 750;
    318. }
    319. },
    320. myplaneisbroke: function () {
    321. for (let i = 0; i < enemyarr.length; i++) {
    322. if (enemyarr[i][0] < myplaneX myplane.width / 2 && enemyarr[i][1] + enemyarr[i][2] < myplaneY myplane.height / 2 + myplane.height) {
    323. if (enemyarr[i][0] + enemyall[enemyarr[i][3]].width > myplaneX myplane.width / 2 && enemyarr[i][1] + enemyarr[i][2] + enemyall[enemyarr[i][3]].height > myplaneY myplane.height / 2) {
    324. obj.myplaneboom();
    325. }
    326. } else if (enemyarr[i][0] > myplaneX myplane.width / 2 && enemyarr[i][1] + enemyarr[i][2] < myplaneY myplane.height / 2 + myplane.height) {
    327. if (enemyarr[i][0] < myplaneX myplane.width / 2 + myplane.width && enemyarr[i][1] + enemyarr[i][2] + enemyall[enemyarr[i][3]].height > myplaneY myplane.height / 2) {
    328. obj.myplaneboom();
    329. }
    330. }
    331. }
    332. },
    333. enemyboom: function () {
    334. var result = [];
    335. for (let i = 0; i < enemychangearr.length; i++) {
    336. enemychangearr[i][3]++;
    337. if (enemychangearr[i][3] >= 10) {
    338. enemychangearr[i][5].src = `img/enemy${enemychangearr[i][2]}boom${enemychangearr[i][4]}.png`;
    339. enemychangearr[i][4]++;
    340. enemychangearr[i][3] = 0;
    341. }
    342. context.drawImage(enemychangearr[i][5], enemychangearr[i][0], enemychangearr[i][1]);
    343. if (enemychangearr[i][4] < 6) {
    344. result.push(enemychangearr[i]);
    345. }
    346. };
    347. enemychangearr = result;
    348. },
    349. enemyisbroke: function () {
    350. for (let i = 0; i < bulletarr.length; i++) {
    351. for (let x = 0; x < enemyarr.length; x++) {
    352. if (bulletarr[i][0] < enemyarr[x][0] && bulletarr[i][1] bulletarr[i][2] < enemyarr[x][1] + enemyarr[x][2] + enemyall[enemyarr[x][3]].height && bulletarr[i][1] bulletarr[i][2] > enemyarr[x][1] + enemyarr[x][2]) {
    353. if (bulletarr[i][0] + bullet.width > enemyarr[x][0]) {
    354. enemyarr[x][4]–;
    355. if (enemyarr[x][4] == 0) {
    356. var enemyboom = new Image();
    357. enemychangearr.push([enemyarr[x][0], enemyarr[x][1] + enemyarr[x][2], enemyarr[x][3] + 1, 0, 1, enemyboom]);
    358. if (enemyarr[x][3] == 3) {
    359. obj.score += 10;
    360. } else {
    361. obj.score += 2;
    362. }
    363. enemyarr.splice(x, 1);
    364. }
    365. bulletarr.splice(i, 1);
    366. }
    367. } else if (bulletarr[i][0] > enemyarr[x][0] && bulletarr[i][1] bulletarr[i][2] < enemyarr[x][1] + enemyarr[x][2] + enemyall[enemyarr[x][3]].height && bulletarr[i][1] bulletarr[i][2] > enemyarr[x][1] + enemyarr[x][2]) {
    368. if (bulletarr[i][0] < enemyarr[x][0] + enemyall[enemyarr[x][3]].width) {
    369. enemyarr[x][4]–;
    370. if (enemyarr[x][4] == 0) {
    371. var enemyboom = new Image();
    372. enemychangearr.push([enemyarr[x][0], enemyarr[x][1] + enemyarr[x][2], enemyarr[x][3] + 1, 0, 1, enemyboom]);
    373. if (enemyarr[x][3] == 3) {
    374. obj.score += 10;
    375. } else {
    376. obj.score += 2;
    377. }
    378. enemyarr.splice(x, 1);
    379. }
    380. bulletarr.splice(i, 1);
    381. }
    382. }
    383. }
    384. }
    385. },
    386. warning: function () {
    387. warningchange++;
    388. warningtime++;
    389. if (warningtime >= 20) {
    390. context.drawImage(warning1, 150, 200);
    391. context.drawImage(warning2, 190, 400);
    392. if (warningtime >= 80) {
    393. warningtime = 0;
    394. }
    395. }
    396. if (warningchange == 500) {
    397. obj.warnon = 0;
    398. obj.bosstime = 1;
    399. }
    400.  
    401. },
    402. bossbgy1: 2420,
    403. bossbgy2: 1640,
    404. bossbgy3: 860,
    405. bg2boss: 262,
    406. bosstimeblur: true,
    407. bossbgon: function () {
    408. context.drawImage(bossbg, 0, this.bossbgy1)
    409. context.drawImage(bossbg, 0, this.bossbgy2);
    410. context.drawImage(bossbg, 0, this.bossbgy3);
    411. if (obj.bosstime == 1) {
    412. context.drawImage(boss, 0, this.bg2boss);
    413. }
    414.  
    415. },
    416. bossbgchange: function () {
    417. this.bossbgy1 += 2;
    418. this.bossbgy2 += 2;
    419. this.bossbgy3 += 2;
    420. this.bg2boss += 2;
    421. if (this.bg2boss == 800) {
    422. this.bosstime = 0;
    423. this.bossattack = 1;
    424. };
    425. if (this.bossbgy3 == 800) {
    426. this.bossbgy1 = 1540;
    427. this.bossbgy2 = 760;
    428. this.bossbgy3 = 20;
    429. }
    430. },
    431. // bossY : -262,
    432. // bossX : 0,
    433. // bossfirstime : 0,
    434. // bosssecondtime : 0,
    435. // beatboss : function(){
    436. // context.drawImage(boss,this.bossX,this.bossY);
    437. // if(obj.bossY <= 0){
    438. // obj.bossfirstime ++;
    439. // if( obj.bossfirstime == 4){
    440. // obj.bossY++;
    441. // obj.bossfirstime = 0;
    442. // }
    443. // }else{
    444. // obj.bosssecondtime++;
    445. // if(obj.bosssecondtime == 2){
    446. // obj.bossX++;
    447. // obj.bossfirstime = 0;
    448. // }
    449. // }
    450. // },
    451.  
    452.  
    453. gua: function () {
    454. enemychangearr = [];
    455. for (let x = 0; x < enemyarr.length; x++) {
    456. var enemyboom = new Image();
    457. enemychangearr.push([enemyarr[x][0], enemyarr[x][1] + enemyarr[x][2], enemyarr[x][3] + 1, 0, 1, enemyboom]);
    458. }
    459. enemyarr = [];
    460. }
    461. }
    462.  
    463. setInterval(function () {
    464. obj.bgon();
    465. obj.bgchange();
    466. if (obj.gamestart == 1) {
    467. obj.starting();
    468. }
    469. if (obj.gameload == 1) {
    470. if (loadrect >= 397) {
    471. obj.gameload = 0;
    472. obj.gamerun = 1;
    473. }
    474. obj.loading();
    475. obj.loadtext();
    476. }
    477. if (obj.gamerun == 1) {
    478. if (obj.score >= 300 && obj.bosstimeblur == true) {
    479. obj.warnon = 1;
    480. obj.gua();
    481. obj.bosstimeblur = false;
    482. }
    483. if (obj.bosstime == 1 || obj.bossattack == 1) {
    484. obj.bossbgon();
    485. obj.bossbgchange();
    486. }
    487. if (obj.dead == 0) {
    488. context.drawImage(myplane, myplaneX myplane.width / 2, myplaneY myplane.height / 2);
    489. if (obj.bosstime == 0 && obj.warnon == 0) {
    490. obj.enemy();
    491. obj.enemychange();
    492. }
    493. obj.bulleton();
    494. obj.bulletchange();
    495. if (obj.warnon == 1) {
    496. obj.warning();
    497. }
    498. }
    499. obj.myplaneisbroke();
    500. obj.enemyisbroke();
    501. obj.enemyboom();
    502. obj.lifeing();
    503. obj.gameovering();
    504. obj.scoring();
    505. }
    506.  
    507. }, 10)
    508.  
    509. var canvasdiv = document.getElementsByClassName(“canvasdiv”)[0];
    510. canvasdiv.onclick = function () {
    511. canvasdiv.className = “canvasdiv none”;
    512. obj.gamestart = 0;
    513. obj.gameload = 1;
    514. }
    515.  
    516. canvas1.onmousemove = function (e) {
    517. if (obj.gamerun == 1 && obj.dead == 0) {
    518. obj.myplane(e);
    519. this.style.cursor = “none”;
    520. } else {
    521. this.style.cursor = “”;
    522. }
    523. }
    524. document.onkeydown = function (event) {
    525. if (event.keyCode == 8 && obj.gamerun == 1) {
    526. obj.gua();
    527. };
    528. }

    HTML5全民飞机大战小游戏是一款突破经典的飞行射击类精品网页小游戏,鼠标操作。

    猜你在找

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    大资源
    一个高级程序员模板开发平台
    • 2026-06-05Hi,初次和大家见面了,请多关照!

    发表评论

    售后服务:

    • 售后服务范围 1、商业模板使用范围内问题免费咨询
      2、源码安装、模板安装(一般 ¥50-300)服务答疑仅限SVIP用户
      3、单价超过200元的模板免费一次安装,需提供服务器信息。
      付费增值服务 1、提供dedecms模板、WordPress主题、discuz模板优化等服务请详询在线客服
      2、承接 WordPress、DedeCMS、Discuz 等系统建站、仿站、开发、定制等服务
      3、服务器环境配置(一般 ¥50-300)
      4、网站的问题处理(需额外付费,500元/次/质保三个月)
      售后服务时间 周一至周日(法定节假日除外) 9:00-23:00
      免责声明 本站所提供的模板(主题/插件)等资源仅供学习交流,若使用商业用途,请购买正版授权,否则产生的一切后果将由下载用户自行承担,有部分资源为网上收集或仿制而来,若模板侵犯了您的合法权益,请来信通知我们(Email: 3482249445@qq.com),我们会及时删除,给您带来的不便,我们深表歉意!

    Hi, 如果你对这款模板有疑问,可以跟我联系哦!

    联系作者
    • 7031会员总数(位)
    • 163602资源总数(个)
    • 2712本周发布(个)
    • 2102今日发布(个)
    • 3687稳定运行(天)

    开通VIP 知识课堂
    升级SVIP尊享更多特权立即升级
    My title page contents
    召唤伊斯特瓦尔