一个脆弱的,愚蠢的,梦幻的排字员决定成为一名程序员,却一无所获……但是他没有放弃编程,而是决定从小程序开始……这是我能想到的最好的描述。出于这个目的,我开始编写简单的程序来磨练自己的技能,以惯用的语言熟悉新的设计,老实说,它甚至开始带给我乐趣。如果您没有开发经验,那么本文将是有用的;如果您已经有开发经验,那么请花一些时间来做一些更有价值的事情。这不是训练。更像是博客。目标是制作游戏井字游戏的3个版本。1-最简单的方法(使用DOM则没有美观的外观)2-有机会一起玩(一台电脑)3-将所有内容转移到画布上我不会描述井字游戏,希望大家都知道游戏的原理。所有有用的链接(存储库,文档)将在本文的结尾。这是怎么回事?嗯...第一版
这是最简单的。老实说,后续版本并不难...我们需要容器的布局,我们需要在其中放置游戏环境。我向每个元素添加了数据项,因为 我以为我需要一个标识符,但是我没有使用它。<div class="app">
<div class="app_block" data-item="0"></div>
<div class="app_block" data-item="1"></div>
<div class="app_block" data-item="2"></div>
<div class="app_block" data-item="3"></div>
<div class="app_block" data-item="4"></div>
<div class="app_block" data-item="5"></div>
<div class="app_block" data-item="6"></div>
<div class="app_block" data-item="7"></div>
<div class="app_block" data-item="8"></div>
</div>
我想立即警告您!此代码不应被视为唯一的代码,否则编写将被视为错误。这是我的解决方案,仅此而已。所以。首先,我们需要在单元格上绑定一个单击。在点击过程中,我们去了(机器人也是,但又是机器人)并检查了单元。var items = document.getElementsByClassName("app_block");
var movePlayer = true;
var game = true;
for (var i = 0; i < items.length; i++) {
items[i].addEventListener("click", function() {
var collecion = document.querySelectorAll(".app_block:not(.active)");
if(collecion.length == 1) {
exit({win: "other"});
}
if( !this.classList.contains("active") ){
if( movePlayer) {
if(this.innerHTML == "") {
this.classList.add("active");
this.classList.add("active_x");
this.innerHTML = "x"
}
var result = checkMap();
if( result.val) {
game = false;
setTimeout(function() {
exit(result);
}, 10);
}
movePlayer = !movePlayer;
}
if(game) {
setTimeout(function() {
botMove();
}, 200);
}
}
});
}
机器人随机行走。function botMove() {
var items = document.querySelectorAll(".app_block:not(.active)");
var step = getRandomInt(items.length);
items[ step ].innerHTML = "0";
items[ step ].classList.add("active");
items[ step ].classList.add("active_o");
var result = checkMap();
if( result.val) {
setTimeout(function() {
exit(result);
}, 1);
}
movePlayer = !movePlayer;
}
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
单元格检查function checkMap() {
var block = document.querySelectorAll(".app_block");
var items = [];
for (var i = 0; i < block.length; i++) {
items.push(block[i].innerHTML);
}
if ( items[0] == "x" && items[1] == 'x' && items[2] == 'x' ||
items[3] == "x" && items[4] == 'x' && items[5] == 'x' ||
items[6] == "x" && items[7] == 'x' && items[8] == 'x' ||
items[0] == "x" && items[3] == 'x' && items[6] == 'x' ||
items[1] == "x" && items[4] == 'x' && items[7] == 'x' ||
items[2] == "x" && items[5] == 'x' && items[8] == 'x' ||
items[0] == "x" && items[4] == 'x' && items[8] == 'x' ||
items[6] == "x" && items[4] == 'x' && items[2] == 'x' )
return { val: true, win: "player"}
if ( items[0] == "0" && items[1] == '0' && items[2] == '0' ||
items[3] == "0" && items[4] == '0' && items[5] == '0' ||
items[6] == "0" && items[7] == '0' && items[8] == '0' ||
items[0] == "0" && items[3] == '0' && items[6] == '0' ||
items[1] == "0" && items[4] == '0' && items[7] == '0' ||
items[2] == "0" && items[5] == '0' && items[8] == '0' ||
items[0] == "0" && items[4] == '0' && items[8] == '0' ||
items[6] == "0" && items[4] == '0' && items[2] == '0' )
return { val: true, win: "bot"}
return {val: false}
}
在这里,您可以通过循环编写所有内容。我选择了一种更简单的方法。我的领域永远是静态的。因此,对细胞进行简单检查。值得注意的是,我退还该物件是为了检查以后谁赢了。在对象中,val和win属性。瓦尔负责结束比赛。游戏结束。
function exit(obj) {
alert(obj.win + " - game over");
location.reload();
};
单击期间,我们进行检查以查看checkMap是否返回val:true。如果是这样,请完成游戏。第二版
同一台计算机上的两个播放器。我将单击处理程序的一部分逻辑引入了一个单独的函数中,并将调用上下文传递给该函数,因为我们需要确定单击了哪个按钮。var items = document.getElementsByClassName("app_block");
var movePlayer = true;
var game = true;
for (var i = 0; i < items.length; i++) {
items[i].addEventListener("click", function() {
var collecion = document.querySelectorAll(".app_block:not(.active)");
if(collecion.length == 1) {
exit({win: "other"});
}
if( !this.classList.contains("active") ){
if( movePlayer) {
firstPlayer(this);
} else {
secondPlayer(this);
}
}
});
}
我分为两个功能,但是它们具有代码重复功能。理想情况下,除以3。一个主要,另外两个与上下文相关。function firstPlayer(that) {
if(that.innerHTML == "") {
that.classList.add("active");
that.classList.add("active_x");
that.innerHTML = "x"
}
var result = checkMap();
if( result.val) {
game = false;
setTimeout(function() {
exit(result);
}, 10);
}
movePlayer = !movePlayer;
}
function secondPlayer(that) {
if(that.innerHTML == "") {
that.classList.add("active");
that.classList.add("active_o");
that.innerHTML = "0"
}
var result = checkMap();
if( result.val) {
game = false;
setTimeout(function() {
exit(result);
}, 10);
}
movePlayer = !movePlayer;
}
第三版
也许这是最有趣的一点,因为现在,该游戏确实看起来像一个游戏,而不是DOM元素的交互。我选择工作PixiJS。关于这个库的+和-我什么也没说,但是我看了一个例子,其中有60,000个元素,并且它们都是动画的。动画很简单,但是FPS保持在50-60。我喜欢它,然后开始阅读文档。我必须马上说,我对英语的了解很少,很难,但是俄语的文章很少(或者我看起来很差)。我不得不戳,并在谷歌翻译的帮助下度过难关。我只看了Yulia Pucnina撰写的有关“用Pixi js制作胖动画”的有关该主题的报告。该报告来自2014年,您需要了解API可能会发生变化。一只眼睛看文档,另一只眼睛看视频。所以我学习了。编写这样一个简单的原型花了4个小时。接近代码。我们将默认初始化为pixiconst app = new PIXI.Application({
width: 720,
height: 390,
resolution: window.devicePixelRation || 1,
});
document.body.appendChild(app.view);
并创建一个包装器(带有单元格的主容器)并将其放入画布let wrapper = new PIXI.Container();
app.stage.addChild(wrapper);
在循环中,我们创建单元格,为它们设置必要的大小,坐标,然后将默认值作为空字符串添加到单元格,因为 启用容器的交互性标志后,这将在将来派上用场并将处理程序挂在单元上。for (let i = 0; i < 9; i++) {
let container = new PIXI.Container();
let block = new PIXI.TilingSprite( PIXI.Texture.from("images/bg.png") , 240, 130);
container.x = (i % 3) * 240;
container.y = Math.floor(i / 3) * 130;
container.addChild(block);
let text = new PIXI.Text("");
text.anchor.set(0.5);
text.x = container.width / 2;
text.y = container.height / 2;
container.addChild(text);
container.interactive = true;
container.on("mousedown", function () {
addValueInBlock(this);
});
wrapper.addChild(container);
}
addValueInBlock负责每个玩家的进度。除了为每个文本声明自己的样式外,我没有找到更好的方法。那里的颜色改变了,但是我不明白如何改变颜色。每次必须将新样式分配给文本。也有检查细胞。function addValueInBlock(that) {
if(firstPlayer) {
if( that.children[1].text == " " ) {
that.children[1].style = {
fill: "#d64c42",
fontFamily: "Arial",
fontSize: 32,
fontWeight: "bold",
};
that.children[1].text = "x"
firstPlayer = !firstPlayer;
}
} else {
if( that.children[1].text == " " ) {
that.children[1].style = {
fill: "#e2e3e8",
fontFamily: "Arial",
fontSize: 32,
fontWeight: "bold",
};
that.children[1].text = "0"
firstPlayer = !firstPlayer;
}
}
endGame();
}
关于验证本身。checkMap。据我了解,pixiJS无法通过名称或ID访问元素。因此,我们必须对容器中的整个集合进行排序,因此代码看起来很麻烦。该函数与前一个函数没有什么不同,除了它返回的参数。function checkMap() {
let items = wrapper.children;
if ( items[0].children[1].text == "x" && items[1].children[1].text == 'x' && items[2].children[1].text == 'x' ||
items[3].children[1].text == "x" && items[4].children[1].text == 'x' && items[5].children[1].text == 'x' ||
items[6].children[1].text == "x" && items[7].children[1].text == 'x' && items[8].children[1].text == 'x' ||
items[0].children[1].text == "x" && items[3].children[1].text == 'x' && items[6].children[1].text == 'x' ||
items[1].children[1].text == "x" && items[4].children[1].text == 'x' && items[7].children[1].text == 'x' ||
items[2].children[1].text == "x" && items[5].children[1].text == 'x' && items[8].children[1].text == 'x' ||
items[0].children[1].text == "x" && items[4].children[1].text == 'x' && items[8].children[1].text == 'x' ||
items[6].children[1].text == "x" && items[4].children[1].text == 'x' && items[2].children[1].text == 'x' ) {
return {active: true, win: "player 1"};
}
if ( items[0].children[1].text == "0" && items[1].children[1].text == '0' && items[2].children[1].text == '0' ||
items[3].children[1].text == "0" && items[4].children[1].text == '0' && items[5].children[1].text == '0' ||
items[6].children[1].text == "0" && items[7].children[1].text == '0' && items[8].children[1].text == '0' ||
items[0].children[1].text == "0" && items[3].children[1].text == '0' && items[6].children[1].text == '0' ||
items[1].children[1].text == "0" && items[4].children[1].text == '0' && items[7].children[1].text == '0' ||
items[2].children[1].text == "0" && items[5].children[1].text == '0' && items[8].children[1].text == '0' ||
items[0].children[1].text == "0" && items[4].children[1].text == '0' && items[8].children[1].text == '0' ||
items[6].children[1].text == "0" && items[4].children[1].text == '0' && items[2].children[1].text == '0' ) {
return {active: true, win: "player 2"};
}
return {active: false};
}
好吧,最后两个功能负责结束游戏并清洁画布。在我看来,这里的解释是多余的。function endGame() {
var result = checkMap();
console.log(result);
if( result.active ) {
setTimeout(function() {
alert(result.win + " - win");
clearMap();
}, 100);
}
}
function clearMap() {
console.log("sdf");
let items = wrapper.children;
for(let i = 0; i < items.length; i++) {
console.log( items[i].children[1].text );
items[i].children[1].text = "";
firstPlayer = true;
}
}
总而言之,分几个阶段进行开发很有趣。这不是理想的开发周期,但需要我开始。感谢您的阅读,很快再见。参考文献
Github报告。PixiJS网站