likes
comments
collection
share

开发个「英雄杀记牌器」

作者站长头像
站长
· 阅读数 88

背景

2020年,那时候很喜欢玩英雄杀,当时游戏主要推广2V2模式,还需要ban pick武将,竞技性还是很强的。

普通人玩游戏不会记牌,但是如果你想赢,一定要记牌,普通的人会记某某装备牌出现过没有、杀闪药还有几张,牛逼的人会记剩下的牌的牌类型、花色、数字,例如他记得剩下的杀是黑桃A、黑桃J、红桃K,药是……。太牛逼了。

当时我要参加一个全国巅峰赛,所以就手写了一个记牌器,帮助我记牌堆中还剩什么牌:tool.hullqin.cn/yxs

最终,也和队友取得了全国第八的成绩,还算满意了,最后一把BP运气太差,我们选的武将必被对方克制,因为不管选啥,武将堆中都有非常克制我们的武将,太难了。

功能介绍

英雄杀的牌,分为3种:「摸牌堆」、「场上的牌」、「弃牌堆」。

  1. 起初,所有牌都在「摸牌堆」,各种牌的初始数量设置为各个牌的总数。
  2. 允许将一些牌放置到「场上的牌」区域,例如你和队友的手牌、装备区的牌、判定区的牌。
  3. 允许将一些牌放置到「弃牌堆」,出的牌、判定牌、判定区的牌解除后,都会扔进弃牌堆。
  4. 允许用户随意设置牌的位置,例如移动「场上的牌」至「弃牌堆」、移动「弃牌堆」至「摸牌堆」等,以适应战场上的多变性。
  5. 允许洗牌。洗牌时,所有「弃牌堆」的牌进入「摸牌堆」。「场上的牌」在洗牌时,不会被清洗,依然留在场上。
  6. 允许清空。相当于重开一局游戏,把所有「弃牌堆」、「场上的牌」都移动至「摸牌堆」。
  7. 「场上的牌」可以随意拖动位置,是为了方便观察(如果把场上的牌跟手牌的顺序对应起来,就很方便了)。

整体界面如下:

开发个「英雄杀记牌器」

开发

HTML和CSS

不再罗列了,可以直接检查源代码:tool.hullqin.cn/yxs

全局变量定义

const total = {
  sha: 15, shan: 7, jiu: 2, yao: 3,
  fhly: 2, wjqf: 1, jd: 2, jdsr: 1,
  fdcx: 3, tnqw: 2, wgfd: 1, wzsy: 1, wxkj: 2, hdwl: 1, spl: 1, blcd: 1,
  hf: 1, xhf: 1, lld: 1, lyq: 1, blc: 1, jgm: 1, fym: 1, yry: 1, qkd: 1
};
const using = {
  sha: 0, shan: 0, jiu: 0, yao: 0,
  fhly: 0, wjqf: 0, wgfd: 0, fdcx: 0, tnqw: 0, wzsy: 0, jdsr: 0, jd: 0, wxkj: 0, hdwl: 0, spl: 0, blcd: 0,
  hf: 0, xhf: 0, lld: 0, lyq: 0, blc: 0, jgm: 0, fym: 0, yry: 0, qkd: 0
};
const used = {
  sha: 0, shan: 0, jiu: 0, yao: 0,
  fhly: 0, wjqf: 0, wgfd: 0, fdcx: 0, tnqw: 0, wzsy: 0, jdsr: 0, jd: 0, wxkj: 0, hdwl: 0, spl: 0, blcd: 0,
  hf: 0, xhf: 0, lld: 0, lyq: 0, blc: 0, jgm: 0, fym: 0, yry: 0, qkd: 0
};
const used_cards = [];
const using_cards = [];
const PREFIX = 'img/';

其中total标识所有牌的总数,using表示「场上的牌」的数量,used表示「弃牌堆」的数量,used_cards表示「弃牌堆」的所有牌,按序排列,using_cards表示「场上的牌」所有牌,按序排列。

另外PREFIX是所有静态资源的前缀(静态资源主要是图片)。

初始化

本段逻辑主要是为了实现这种效果:

  • 通过图片展示牌的类型。
  • 可以展示「摸牌堆」实时剩余多少张。
  • 为了让「摸牌堆」剩多少张展示更生动形象,还加了绿色的百分比进度条。
  • 通过+1-1按钮,可以操控:把「摸牌堆」的牌移动到「场上的牌」、或把「摸牌堆」的牌移动到「弃牌堆」。

开发个「英雄杀记牌器」

function html_init() {
  const main = document.getElementById('main');
  const used = document.getElementById('used');
  for (key in total) {
    const key2 = key;
    const dcard = document.createElement('div');
    dcard.className = 'dcard';
    const dimg = document.createElement('div');
    dimg.className = 'dimg';
    const img = document.createElement('img');
    img.alt = key;
    img.style.height = '100%';
    img.src = PREFIX + key + '.jpg';
    img.onclick = function(){update(key2, 'using', 1);};
    img.oncontextmenu = function (e) {
      e.preventDefault();
      update(key2, 'used', 1);
    };
    dimg.appendChild(img);
    dimg.style.textAlign = 'center';
    const dleft = document.createElement('div');
    dleft.className = 'dleft';
    let text = document.createElement('span');
    text.innerHTML = '牌堆剩 ';
    dleft.appendChild(text);
    const sleft = document.createElement('span');
    const sleft2 = document.createElement('span');
    sleft2.id = key + '_left';
    sleft.style.display = 'flex';
    sleft.style.border = '1px solid';
    sleft.style.flexGrow = '1';
    sleft2.style.width = '0';
    sleft2.style.background = '#98da98';
    sleft.appendChild(sleft2);
    dleft.appendChild(sleft);
    const dusing = document.createElement('div');
    dusing.className = 'dusing';
    text = document.createElement('span');
    text.innerHTML = '场上:';
    dusing.appendChild(text);
    const susing = document.createElement('span');
    susing.id = key + '_using';
    dusing.appendChild(susing);
    const susing2 = document.createElement('span');
    susing2.className = 'sbtn';
    const btn1 = document.createElement('button');
    btn1.className = 'b1';
    btn1.onclick = function(){update(key2, 'using', 1);};
    btn1.innerHTML = '+1';
    susing2.appendChild(btn1);
    const btn2 = document.createElement('button');
    btn2.className = 'b-1';
    btn2.onclick = function(){update(key2, 'using', -1);};
    btn2.innerHTML = '-1';
    susing2.appendChild(btn2);
    dusing.appendChild(susing2);
    const dused = document.createElement('div');
    dused.className = 'dused';
    text = document.createElement('span');
    text.innerHTML = '弃牌:';
    dused.appendChild(text);
    const sused = document.createElement('span');
    sused.id = key + '_used';
    dused.appendChild(sused);
    const sused2 = document.createElement('span');
    sused2.className = 'sbtn';
    const btn3 = document.createElement('button');
    btn3.className = 'b1';
    btn3.onclick = function(){update(key2, 'used', 1);};
    btn3.innerHTML = '+1';
    sused2.appendChild(btn3);
    const btn4 = document.createElement('button');
    btn4.className = 'b-1';
    btn4.onclick = function(){update(key2, 'used', -1);};
    btn4.innerHTML = '-1';
    sused2.appendChild(btn4);
    dused.appendChild(sused2);
    dcard.appendChild(dimg);
    dcard.appendChild(dleft);
    dcard.appendChild(dusing);
    dcard.appendChild(dused);
    dcard.id = 'main_' + key;
    main.appendChild(dcard);
  }
}
function update_all() {
  for (let key in total) {
    update_one(key);
  }
  update_total_left();
}

添加一个「场上的牌」

function add_one_using(card) {
  using_cards.push(card);
  const eusing = document.getElementById('using');
  const div = document.createElement('div');
  const img = document.createElement('img');
  const guid = new Date().valueOf().toString();
  img.src = PREFIX + card + '.jpg';
  img.style.height = '100%';
  img.alt = card;
  img.draggable = false;
  img.onclick = function() {
    if (! update(card, 'using', -1, guid)) update(card, 'used', 1);
  };
  img.oncontextmenu = function(e) {
    e.preventDefault();
    update(card, 'using', -1, guid);
  };
  div.className = 'using-dimg';
  div.draggable = true;
  div.setAttribute('name', 'using_' + card);
  div.id = guid;
  div.appendChild(img);
  div.ondragstart = function(e) {e.dataTransfer.setData("Text", e.target.id);};
  div.ondragover = function(e) {e.preventDefault();};
  div.ondrop = function(e) {
    e.preventDefault();
    const data = e.dataTransfer.getData("Text");
    if (data.length < 6) return;
    const src = document.getElementById(data);
    if (src.getAttribute('name').substr(0, 4) === 'used') return;
    const dst = e.target.parentNode;
    if (src.offsetLeft < dst.offsetLeft) {
      dst.parentNode.insertBefore(src, dst);
    } else if (src.offsetLeft > dst.offsetLeft) {
      dst.parentNode.insertBefore(src, dst.nextElementSibling);
    }
  };
  eusing.insertBefore(div, eusing.firstChild.nextSibling.nextSibling);
}

删除一个「场上的牌」

function del_one_using(index, guid=null) {
  if (guid === null) {
    document.getElementsByName('using_' + using_cards[index])[0].remove();
  } else {
    document.getElementById(guid).remove();
  }
  using_cards.splice(index, 1);
}

添加一个「弃牌堆」的牌

function add_one_used(card) {
  used_cards.push(card);
  const eusing = document.getElementById('used');
  const div = document.createElement('div');
  const a = document.createElement('a');
  const img = document.createElement('img');
  const guid = new Date().valueOf().toString();
  img.src = PREFIX + card + '.jpg';
  img.style.height = '100%';
  img.alt = card;
  img.ondragstart = function(e) {e.dataTransfer.setData("Text", e.target.parentNode.parentNode.id);};
  a.onclick = function(){
    if (! update(card, 'used', -1, guid)) update(card, 'using', 1);
  };
  a.appendChild(img);
  div.className = 'used-dimg';
  div.setAttribute('name', 'used_' + card);
  div.id = guid;
  div.appendChild(a);
  eusing.appendChild(div);
}

删除一个「弃牌堆」的牌

function del_one_used(index, guid=null) {
  if (guid === null) {
    document.getElementsByName('used_' + used_cards[index])[0].remove();
  } else {
    document.getElementById(guid).remove();
  }
  used_cards.splice(index, 1);
}

备注

这是以前写的代码了,比较粗糙,但是至少实现了「英雄杀记牌器」的功能。感兴趣的可以去网站看一下,都是原生JS,没有混淆。

写在最后

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》《极致用户体验》

转载自:https://juejin.cn/post/7135843844922441741
评论
请登录