览器和 JavaScript 的功能逐年不断的变强变大。曾几何时,任何类型的游戏都需要Flash。但随着 HTML5 发展,HTML5 + WebGL 游戏式就慢慢占领着这个舞台。以下是30款流行的游戏,它们可以在所有现代浏览器中运行,并且只使用web技术构建。
地址:http://hexgl.bkcore.com/
类型:街机,赛车
HexGL 是一款基于HTML5,JavaScript和WebGL的快节奏的赛车游戏。玩家可以使用键盘,触摸屏设备或leap motion(体感控制器)来控制太空飞船。
地址:http://www.cross-code.com/en/home
类型:动作,角色扮演
一个复古灵感的2D游戏设定在遥远的未来。这是一个充满伟大的游戏机制,如组合,拼图,技能树,任务,物品等等。
地址:https://sketch-out.appspot.com/
类型:街机
Sketchout的任务保护你的行星,并通过改变流星的方向来消灭对手,通过使流星偏转来保护您的星球并消灭对方,这款游戏有很棒的视觉效果和音乐特效。
地址:http://www.treasurearena.com/类型:多人,角色扮演,动作
Treasure Arena 是一款动态的竞技场战斗游戏,最多可容纳4名玩家。它具有不同的游戏模式,出色的帧率和配乐,是一个非常有趣的游戏。
地址:http://bejeweled.popcap.com/html5/
类型:街机,解谜,娱乐
HTML5格式的经典“宝石迷阵”游戏。这是一个官方克隆,因此可以正常运行且外观完美。
地址:http://missile-game.bwhmather.com/类型:街机
这是一款非常具有挑战性的游戏,游戏中我们扮演的是一枚被发射进隧道的导弹。游戏有很酷的黑白图像,玩的时候会有很强的场景效果。
地址:http://www.deconstructeam.com/games/gods-will-be-watching/类型:拼图
在这个令人毛骨悚然(但又很棒)的游戏中,我和自己团队必须独自生存40天。团队有六名成员,其中包括一只狗,一名精神病医生和一个机器人,您必须与他们互动,以使其保持温暖,温饱和理智的状态。
地址:http://www.sinuousgame.com/类型:街机
一个简单的游戏,极简的图形和流畅的帧率。拾取电源时避免与红点碰撞。此外,如果你想要那些额外的积分,就需要不停向前移动
地址:http://swooop.playcanvas.com/类型:街机
在一个美丽多彩的3D世界里,到处飞翔,收集宝石和星星。
地址:http://www.freeriderhd.com/
Free Rider HD 是一款令人上瘾的游戏,你可以在其他玩家绘制的赛道上骑自行车。可以在成千上万的播放器曲目中选择一个播放,也可以创建自己的曲目并分享。
地址:http://entanglement.gopherwoodstudios.com/zh-CN-index.html类型:拼图,娱乐
这个游戏的目的是通过在网格上放置线段来创建一条尽可能长的路径。你可以单独玩,也可以和朋友一起玩。
地址:https://www.modern.ie/en-us/ie6countdown#escape-from-xp
类型:动作,街机
用“Escape from XP”来庆祝 Windows XP 的终结。你的任务是拯救最后一个陷入Clippy暴政的开发人员。
地址:http://polycraftgame.com/类型:角色扮演,塔防,动作
在这个很棒的3D游戏中,你到处收集资源,建造东西,完成任务。关于它的所有东西都经过抛光,并且运行也非常顺畅。
地址:https://gabrielecirulli.github.io/2048/类型:拼图
一个非常上瘾的游戏,你可能已经玩过了。在 2048 ,你移动编号的图块并合并它们。当界面中最大数字是`2048 时,游戏胜利。
地址:http://arcade.lostdecadegames.com/onslaught_arena/
类型:动作
一种快节奏的复古生存游戏,您可以使用不同的武器与成群的敌人作战。
地址:http://chrome.angrybirds.com/类型:游戏
《愤怒的小鸟》游戏,这就不用介绍了。
地址:https://www.cubeslam.com/mcycrs
类型:街机,多人
具有丰富的色彩和炫酷的3D图形乒乓球游戏。我们可以通过向朋友发送一个URL来挑战他们,还可以通过网络摄像头看到对方。
地址:http://hypnoticowl.com/games/the-wizard/类型:动作,角色扮演,策略
Wizard 是基于回合的地牢爬行者,在里面会遇到神话般的怪物并找到奇妙的咒语。该游戏具有酷炫的战斗机制,有时可能会带来很大挑战。
地址:http://phoboslab.org/xtype/类型:动作,街机
在这款酷炫的太空射击游戏中,你目的就是要起战胜 Boss。
地址:http://orteil.dashnet.org/cookieclicker/类型:休闲,搞笑
Cookie clicker 是一款可能为了开玩笑而创建的游戏,但仍然提供了大量的乐趣。你可以从0个cookie开始,然后单击一些有效率的cookie,最后你可能会发现自己拥有数十亿个cookie。
地址:http://play.elevatorsaga.com/类型:拼图,编码
这类属于程序员类型游戏 。在电梯中的任务是通过对电梯的运动进行编程,以最有效的方式运送人员,这些都是用 JavaScript 来完成的。
地址:http://gameofbombs.com/landing类型:动作,角色扮演,多人
Game of Bombs是一个轰炸机类型的游戏,在广阔地图上,都有着敌方玩家。收集力量,皮肤和成就,以成为最佳轰炸机玩家的方式。
地址:http://or.paleozoic.com/类型:平台游戏,动作
Olympia Rising具有漂亮复古外观图形的游戏。它坐落在古希腊,在那里我们扮演的女人被赋予了重新的机会,所以我们的任务就是逃离死者的世界。
地址: https://ned.im/pixel-race-game/类型:街机,赛车
Pixel Race是一款简单概念概念,你可以在收集硬币的同时控制汽车以避开障碍物。如果有足够的耐心和空闲时间,那么你可能会打破记录(记录为36309个硬币)。
地址:https://littlealchemy.com/类型:拼图
从这四个基本元素开始,将它们组合起来,创建510种可能的组合。
地址:http://www.kevs3d.co.uk/dev/arena5/类型:街机
在数字领域中飞行并射击几何敌人以获得高分。
地址:https://vector-runner-remix.tresensa.com/
类型:街机
在这个充满色彩和几何形状的平台游戏中,尽你所能奔跑吧。
地址:http://playbiolab.com/类型:动作
一款出色的像素艺术平台游戏,你必须在这里逃脱充满突变生物和其他不良生物的实验室。
地址:http://worldsbiggestpacman.com/#类型:街机
地址:http://games.jessefreeman.com/new-super-resident-raver/
从即将到来的僵尸入侵中拯救惊慌失措的人们。收集钱,升级你的武器和战斗僵尸。
作者:Danny Markov 来源:tutorialzin 译者:前端小智
原文:https://tutorialzine.com/2015/02/30-amazing-games-made-only-with-html5
发语言:js
实例大小:5.05M
实例类别:网页游戏
【核心代码】Html JavaScript游戏源码
Runner.loadImage = function()
{
var img = new Image();
img.onload = Runner.imgLoadHandler;
img.src = imageURLs.shift();
ImageManager.push(img);
document.getElementById(“status”).innerHTML = “Loading(” loaded “/6)” ": " img.src;
}
gif 图较大,耐心等待,源码见文末
为了上班摸鱼合理的玩游戏,我写了一个3d塔防游戏,其中功能包含动画、敌人运动、放置武器、升级武器、销毁武器、动态检测等功能。请动动小手,点赞收藏,这就发车~
具体功能和思路如下
有了这个思维导图,就可以按部就班,一步一步的实现游戏功能
由于项目体系较大,内容覆盖较广,下面挑几个关键内容介绍一下
首先要加载一个地图,地图功能包含可放置模块,不可放置模块(敌人路线,装饰元素),大概思路就是根据floorSize生成一个长和宽相等的地图,每个地图都是一个plane。
const createPlane = (texture: THREE.Texture): THREE.Mesh => {
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide });
let plane = new THREE.Mesh(geometry, material);
plane.rotation.x = Math.PI * 0.5;
plane.material.map = texture;
plane.material.needsUpdate = true;
castShadow(plane)
return plane
}
第一步用for循环,生成一个二维数组存放在mapUseV2中,设定一个起点const startPoint = new THREE.Vector2(0, 0)和终点const endPoint = new THREE.Vector2(floorSize - 1, floorSize - 1),用于生成敌人运动轨迹。
生成轨迹的代码
const maps = new (window as any).Graph(mapUseV2);
// 夸过阻碍的随机点位,生成怪物的路线图
var starPosition = maps.grid[startPoint.x][startPoint.y];
var endPosition = maps.grid[endPoint.x][endPoint.y];
// 计算路线图
let trailPoints = (window as any).astar.search(maps, starPosition, endPosition, {
closest: false
});
寻路生成后的数据结构如下
这些14个点位都是敌人的路线,所以不可以放置武器,所以要在mapUseV2中将数据置为0
mapUseV2[trailPoints[i].x][trailPoints[i].y] = 0;
这样我们就得到了一个包含敌人行动路线、装饰、可放置区域的地图。
前面定义的startPoint作为敌人的出生点。生成敌人的方法为EnemyCrouched类,初始化敌人的血条,移动速度,攻击力,和等级。
继承基类Enemy_Level,Enemy_Level定义了添加方法、运动、和动画的更新。如果要多加一种敌人类型,再继承Enemy_Level即可,(项目目前只有一种敌人类型),而Enemy_Level也继承了公共类ModelCheck,这个公共类主要功能是修改位置、旋转角度、获取包围盒等功能。武器、子弹、敌人、水晶塔都继承了这个公共类。
创建敌人的时候遇到一个问题,本来想着节约渲染成本,只加载一次敌人模型,再使用clone()方法,复制出多个敌人,但是敌人模型包含了骨骼模型,在克隆模型的时候,骨骼动画和敌人模型复制了,但是两者的绑定关系没有复制成功,导致骨骼动画和模型不匹配,导致动画不生效,所以每次生成敌人的时候都重新加载了一下模型。
export class EnemyCrouched extends Enemy_Level {
coefficient: number = crouched_coefficient
constructor(level: number) {
super(ENEMY_CROUCHED_NAME)
this.speed = this.coefficient * 60
this.price = this.coefficient
this.HP = this.coefficient * level
this.level = level
// 骨骼在执行clone()时候,骨骼之间的位置和动画发生错位,所以每次重新拉一次模型,很不可取。
// 但是可以重新克隆一个骨骼动画,并重新绑定骨骼动画和新的模型之间的关系
loadGltf(import.meta.env.VITE_ASSETS_URL + '/assets/models/snowgolem/scene.gltf').then((gltf: any) => {
this.model = gltf.scene
this.bindAnimite(gltf.animations)
this.addModel()
this.group.example = this
this.handleModel(this.group)
this.run()
this.runStart()
});
}
}
通过bindAnimite绑定动画可以将模型中提供的骨骼动画绑定到模型上并播放动画。
bindAnimite(animations: GltfModel['animations']) {
if (this.model) {
this.modelAnimation = new Animation(this.model, animations);
this.modelAnimation.once(['death'])
this.modelAnimation.play('walking');
}
}
这里说一下Animation类,提供播放动画play、切换动画fadeToAction、显示骨骼createSkeleton,过滤一次性动画once比如跳跃,死亡,敬礼这种非循环播放的动画。 更新动画upDate,这个方法接受一个镜头参数,如果将镜头参数传进来,可以实现镜头跟踪功能。
生成武器通过点击操作栏对应的按钮进行。
在点击十字弓按钮后,将触发鼠标的射线检测,并将检测目标设置为地板,当鼠标移动至可放置模型的位置时,显示蓝色的框,如果检测到鼠标位置不可放置模型,则辅助色块变为红色,点击无效,需重新选择位置。
Crossbow_level初始化十字弓,Rifle_level初始化火炮。这两个类是区分不同的武器,并在初始化的时候加载武器模型并提供武器的upDate方法,这两个类都继承他们的基类Weapon。Weapon提供目标检测、射击等功能。Weapon又继承公共类ModelCheck为武器提供包围盒,尺寸和模型矫正的功能。初始化武器时有几个属性比较重要:武器系数coefficient、等级level这两个数值影响武器的子弹发射间隔,威力,和单价(包括升级的价格)。
这里介绍一下IntervalTime
.setAnimationLoop ( callback : Function ) : undefined
callback — 每个可用帧都会调用的函数。 如果传入‘null’,所有正在进行的动画都会停止。 可用来代替requestAnimationFrame的内置函数. 对于WebXR项目,必须使用此函数。
requestAnimationFrame方法熟悉threejs的都了解,基于屏幕刷新率调用的,一般60次/秒,实现定时的原理很简单,调用一次的时候获取一次当前时间,用当前调用时的时间now减去上次存的时间lastTime,如果超过规定的循环间隔时间time调用一次回调,这时会将lastTime置为time,并进行新一轮的计时。项目中炮弹发射间隔就是这么做的。当然,你也可以在threejs以外的项目使用。
export class IntervalTime {
lastTime = 0
constructor() {
}
interval(time: number, callback: () => void) {
let now = performance.now(); // 使用 performance.now() 获取高精度时间
let deltaTime = now - this.lastTime;
if (deltaTime > time) {
// 执行一秒内需要做的事情
callback()
// 重置时间
this.lastTime = now;
}
}
}
武器在生成后会发射子弹,调用TransmitDiscards类。根据不同的武器生成不同的子弹,并在render中调用子弹类的upDate方法。子弹会跟踪检测敌人的位置,并找到最近的敌人进行攻击,得到最近敌人的位置和子弹发射位置,生成一条抛物线,开口向下,并让子弹沿着抛物线进行运动,直到子弹运动到终点,并利用敌人的包围盒检测子弹的位置,判断子弹是否进入敌人包围盒的范围。如果在范围内,敌人HP掉对应的血量,并销毁子弹。
upDate() {
// 子弹检测敌人
EnemyGroup.traverse((enemy) => {
if (enemy.example) {
if (this.model) {
this.getBox()
const worldPos = new THREE.Vector3()
this.model.getWorldPosition(worldPos)
this.position = worldPos.clone();
enemy.example.getBox();
const contains = enemy.example.box3.containsPoint(this.position)
// 子弹撞击敌人
if (contains) {
console.log('击中敌人');
let hp = enemy.example.HP - this.power
enemy.example.HP = hp
enemy.example.hpDom.element.innerHTML = `HP:${Math.max(0, hp)}`
this.dispose()
}
}
}
})
}
当敌人的hp归零时,则销毁敌人并增加对应数量的金币
敌人的update方法
upDate() {
this.getBox()
if (this.modelAnimation) {
this.modelAnimation.upDate()
}
if (this.HP <= 0) {
this.dispose();
wallet.add(this.price);
}
}
操作栏的功能区,提供了几个属性,区分放置、升级、销毁等功能
// 存放全局变量的
export const buttonState = {
DOWN_HERO: false, // 是否可以放置模型
CHECK_HERO: true, // 是否可以选中模型
HREO_TYPE: 'crossbow' as "crossbow" | "rifle",
UPGRADE_HERO: false, // 是否升级英雄
DISPOSE_HERO: false, // 是否销毁英雄
SHOW_BOX3_HELPER: true, // 是否显示辅助线
GAME_STOP: false // 暂停游戏
}
前面提过,放置武器的时候,射线检测的对象为floor组,而升级则会将射线检测的组改为武器组,通过检测到的模型去升级对应的二级和三级,当武器达到三级的时候则不可再升级,升级武器时将原有武器销毁,添加一个新的模型,目前对于武器升级只是将系数调高,如感兴趣可根据武器等级,生成多个子弹,从单一攻击改为群攻。
相对于水晶塔,则复用子弹的逻辑即可,将敌人视作子弹,水晶塔视作敌人,在水晶塔的upDate方法,检测敌人的位置,如果敌人的包围盒与水晶塔的包围盒相交,则水晶塔减去对应的hp,如果水晶塔的hp归零,则视为游戏失败
金币方法相对简单一些,咱们这里没vip充值,所以不必考虑一个648换算几个钻石,1个钻石换算几个金币,游戏的金币系统,只有一个加一个减,创建和升级武器减去相对应的金币,消灭敌人增加对应的金币
import { Money } from "./variable";
// 钱包实例
class Wallet {
money: number = Money;
moneyDom: HTMLDivElement | undefined
constructor() {
const dom = document.querySelector('.money') as HTMLDivElement | undefined
if (dom) {
this.moneyDom = dom;
this.changeText()
}
}
add(money: number) {
this.money += money;
this.changeText()
}
sub(money: number) {
if (this.money < money) {
return false
} else {
this.money -= money;
this.changeText()
}
return true
}
changeText() {
if (this.moneyDom) {
this.moneyDom.innerText = this.money + '金币'
}
}
}
export const wallet = new Wallet()
金手指支持初始化金币数量、武器系数、地图尺寸。通过路径参数提供
const searchParams = getParams()
const getValue = (field: string, def: number): number => {
console.log('searchParams',searchParams);
const f = searchParams[field]
let fz = f ? Number(f) : def;
return fz
}
export const floorSize = getValue('floorSize', 8) // 必须双数,不然后面的计算有问题
// 系数
// 十字弩系数
export const crossbow_coefficient = getValue('crossbow_coefficient', 10)
// 火炮系数
export const rifle_coefficient = getValue('rifle_coefficient', 10) * 2
// 初始化金币
export const Money: number = getValue('money', 100);
如果将路径地址后面拼上这段参数
?crossbow_coefficient=100&rifle_coefficient=100&floorSize=16&money=500,
那么你将拥有500个金币,攻击力100打底的武器,并且地图尺寸变大,敌人行动轨迹变长
这会影响游戏的平衡性,当然,我们的游戏也没做公平性和平衡性的考虑。
链接:https://juejin.cn/post/7355745761370505231
*请认真填写需求信息,我们会在24小时内与您取得联系。