整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

恶魔的石板和被诅咒的犬公主一款画风超级可爱的2D的日

恶魔的石板和被诅咒的犬公主一款画风超级可爱的2D的日系SLG游戏

魔的石板和被诅咒的犬公主是一款画风超级可爱的2D的日系SLG游戏,游戏内容的制作也相当不错。游戏内可以选择英语、日语和繁体中文三种语言,是官方汉化的版本。游戏采用二次元动漫风格打造而成,拥有十分精美的人物立绘以及场景长途,为你带来更加良好的视觉体验。玩家在游戏中会根据自己不同的操作来解锁出不同的剧情,玩法十分自由,开放式的游戏结局,为你带来许多意想不到的游戏体验!在游戏里,玩家需要在这款手游中,去拯救公主,从而踏上末知的道路。玩家需要攻克重重困难,去拯救公主,将她拯救出来。恶魔石板和被诅咒的犬公主手游设计了经典的宫廷风,偏向西方世界的经典场景。
下载地址:http://www.32r.com/app/78321.html


游戏背景

在很久很久以前,有个看上去就不咋靠谱的王国

王国最大的熊孩子——小公主

因为皮,不小心把自己封在了一面寄宿了恶魔的镜子里面!

这下可完了不仅再也不能到处捣乱,甚至可以预见要在这个鬼镜子里面过一辈子?

国王着急上火心急如焚,甚至一天只能睡8个小时的觉!

(我寻思这也不着急啊……)

于是,他一拍脑袋想出来一个办法

只要能救公主出来,我就将她嫁给这位勇士!

前来尝试的勇者们络绎不绝,但是一个个全都失败了

而你,是个奇怪的中年大叔,带着猥琐的笑容进入了王宫……

不过,寄宿在镜子中的恶魔却想出了一个奇怪的方法决胜负

大名鼎鼎的友情泯灭游戏精神崩溃!


游戏特色

1、游戏中玩家的任务就是想办法去把公主揪出来。

2、拯救公主的路途上会遇到很多的艰难险阻,但那都不是问题。

3、只要我们玩家能有耐心刷级刷装备过关还是很简单的。

4、传统的rpg玩法,以打怪升级为辅,剧情为主,给人 不一样的体验。

5、丰富的游戏内容以及超多的精彩小剧情,让玩家能够捧腹大笑。

6、清新脱俗的游戏画面,卡通简约的游戏场景,带给玩家一种亲切的感觉。

游戏亮点

1、传统的rpg玩法,以打怪升级为辅,剧情为主,给人 不一样的体验。

2、丰富的游戏内容以及超多的精彩小剧情,让玩家能够捧腹大笑。

3、恶魔石板和被诅咒的犬公主有着趣味性十足的游戏剧情,加上可爱的游戏画风,能让人非常喜欢这款游戏。

4、游戏中玩家可以经常和公主对话,每一次都能有一些不一样的对话哦!

游戏优势

1、丰富的养成元素,可以查看角色的各种具体数值,全方位了解人物;

2、有着诸多的变化,屏幕的靓丽特性,互动的十足,期待不同人群的相遇;

3、结合了浪漫的恋爱元素,生动的对话与场景,让玩家获得最真实的体验。


游戏攻略

那么本篇攻略的最大难点和关键就在于知道有多少格子了!知道表格几行几列,就可以把字母对应记下来,由于游戏没有时间限制,你大可以慢慢看哪些字母重复的,就可以翻牌了!另外你和恶魔的挑战必须一次性通关,中途输了只能从头来过,虽然它也有好处可以增加点数?你完全可以失败无数次把前两行点数刷满,不过爱动脑的妹推酱怎么可能硬肝?

第一局: 5列6行,先从第1行最中间开始,然后是最后1行最中间,运气好可以碰上一样的。

第一局按妹推酱的表格5列6行

注意注意!因为前几次失败已经积累了点数可以透视到周围的字母,所以开局没翻很多的时候上下左右就没必要再点一次去看它是啥,反而会给恶魔暴露出你点到的刚想匹配的字母,前期可能还好,后面两局如果继续这样,恶魔也不会手软,连续地把你提供的重复字母给点掉了。(玩过的自然懂)

第二局:6列7行,图片里多画了一行,划了条横线表示不算数。第二局玩的时间最久,因为石碑大小是一样的,里面的方块会变小,所以第二局第一次玩的时候根本不知道几行几列所以很难找准位置记录字母。为此妹推酱的方法是这样的,先不画表格,找准位置,点恶魔形象左边第一行那个位置,记录字母,下左右的字母也出来了,隔一格在右边再点一次,也会有下左右的字母出来,这个时候就可以大致分辨出有几列了。

如图所示,可以肉眼分辨出几行几列,不要盲目往中间凑。

第三局:8行7列,这一把开始虽然越来越难,字母也是加了很多,但方法在手,就是一路由我掌控了,恶魔只点了三回吧,不需要全部字母都知道,只要关注石碑下面,左边蓝色是勇者“我”,成功翻牌一次就得一块蓝块,谁先到最中间谁胜利。

第四局:9行8列,有个小机关,可能其他玩家遇到的不一样,就是有同样的字母K,一个是红色,一个是灰色,两个不能匹配,必须字母颜色一样才行。

这个有点乱,四把一口气都赢了以后恶魔才能认可你,把公主换回来,后续剧情不多赘述。

图小游戏

先来预览,咳咳,这个比上次那个地鼠会好看点……

效果

代码是可以设置难度的,3 就是 9,9 就是 81……

相比来说,此程序难度可是远远高过打地鼠的,希望小伙伴能跟上~

html

<header>
 <button='Game.restart()'>重新开始</button>
 <button id="download"='Game.openImage()'>新标签打开图片</button>
</header>
<main>
 <section class="game-area">
 <img id='background-img' src="#" alt="backgroundImg">
 <div id="cut-imgs"></div>
 </section>
</main>

header 好理解,注意其中的“新标签打开图片”相当于过关福利,平常是隐藏的。

之所以内容这么少,是因为主逻辑这一块的 html 代码许多属性都是动态的,所以写死没有价值,需要在 js 里面动态生成与删除,所以基本都移到 js 里面了,这里只要看到几个容器就行。其中 #cut-imgs 是下面游戏的容器。

css

.cut-img {
 position: absolute;
 top: 0;
 left: 0;
 border: 0;
 padding: 0;
 transition: transform .3s linear;
 box-sizing: border-box;
}
html,
body {
 height: 100%;
 margin: 0;
}
body {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
}
header,
main,
footer {
 width: 50%;
}
header {
 display: flex;
 justify-content: space-between;
}
main {
 position: relative;
 height: auto;
}
.game-area {
 position: relative;
 height: auto;
}
#background-img {
 max-width: 100%;
 max-height: 100%;
 vertical-align: top;
 opacity: 0;
}
#cut-imgs {
 position: absolute;
 top: 0;
 left: 0;
 display: flex;
 flex-wrap: wrap;
 width: 100%;
 height: 100%;
}
button:focus {
 outline: none;
}
.selected {
 border: 1px solid blue;
}
#download {
 display: none;
}

css 里面注意动画的设置,还有切片图像的处理。

这里相信第一反应下面是把一整张图切 9 份。其实不然,不过是 9 个容器(本例用的是 button)分别展示了不同图片的一部分,然后控制相关的容器即可。

所有容器的位置都是左上角,设置偏移量使其在各个位置上,具体设置方法在 js 里面。

js

const Game={
 // 重新开始游戏
 restart() {
 // 清空已有数据,重置按钮
 this.reset()
 const level=this.config.level
 // 计算 position 的参数
 const positionParam=1 / (level - 1) * 100
 const imgUrl=this.config.imgUrl=`https://h5games-dom.oss-cn-hangzhou.aliyuncs.com/puzzle/${~~(Math.random() * 5)}.png`
 const backgroundImg=document.querySelector('#background-img')
 backgroundImg.src=imgUrl
 // 获取样式表
 const styleSheet=this.config.imgCutStyle=document.styleSheets[0]
 // 如果添加过自定义则删除
 let firstRule=styleSheet.rules[0]
 if (firstRule.selectorText==='.custom') styleSheet.deleteRule(0)
 let scale=1 / this.config.level * 100 + '%'
 styleSheet.insertRule(`.custom {
 width: ${scale};
 height: ${scale};
 background: url(${imgUrl}) no-repeat;
 background-size: ${this.config.level * 100}%; }`, 0)
 backgroundImg.=()=> {
 for (let i=0, j=Math.pow(this.config.level, 2); i < j; i++) {
 this.config.cutImgsCountArray.push(i)
 }
 // DOM字符串
 let cutImgsStr=''
 this.getInitialSort()
 this.config.cutImgsCountArray.forEach((num, index)=> {
 // 保存正确的变化,做判断是否获胜的基础
 this.config.trueTransforms.push(`translate(${index % level * 100}%, ${~~(index / level) % level * 100}%)`)
 // 这里设置会变动的 style
 const transform=`transform: translate(${num % level * 100}%, ${~~(num / level) % level * 100}%);`
 const backgroundPosition=`background-position: ${index % level * positionParam}% ${~~(index / level) % level * positionParam}%;`
 // 全部在左上初始位置,设置偏移量即可
 cutImgsStr +=`<button class="cut-img custom" data-index=${index}="Game.click(event)" style="${transform + backgroundPosition}"></button>`
 })
 document.querySelector('#cut-imgs').innerHTML=cutImgsStr
 this.instance.cutImgs=document.querySelectorAll('.cut-img')
 }
 },
 // 点击图片
 click(e) {
 const index=e.target.dataset.index
 // 第一次点击直接结束
 if (this.tool.currentIndex===-1) {
 this.getCutImg(index).classList.add('selected')
 this.tool.currentIndex=index
 return
 }
 const oldCutImg=this.getCutImg(this.tool.currentIndex)
 // 如果点击不是同一个再走逻辑
 if (this.tool.currentIndex===index) {
 this.getCutImg(index).classList.remove('selected')
 this.tool.currentIndex=-1
 } else {
 const newCutImg=this.getCutImg(index)
 const [a, b]=[newCutImg.style.transform, oldCutImg.style.transform]
 oldCutImg.style.transform=a
 newCutImg.style.transform=b
 this.tool.currentIndex=-1
 setTimeout(()=> {
 download.style.display='none'
 oldCutImg.classList.remove('selected')
 newCutImg.classList.remove('selected')
 if (this.checkNoWin()) console.log('NoWin')
 else {
 download.style.display='block'
 alert('win')
 }
 }, 500);
 }
 },
 // 获取实例
 getCutImg(index) {
 return this.instance.cutImgs[index]
 },
 // 获取初始的正确排序
 getInitialSort() {
 const cal=arr=> {
 let length=arr.length
 let reverse=0
 for (let i=0; i < length - 1; i++) {
 let n=arr[i]
 for (let j=i + 1; j < length; j++) {
 let m=arr[j]
 if (n > m) reverse +=1
 }
 }
 return reverse
 }
 // 数组随机排序
 const randomSort=(a, b)=> Math.random() > 0.5 ? -1 : 1
 // 循环直到获取可还原的排序
 while (1) {
 if (cal(this.config.cutImgsCountArray.sort(randomSort)) % 2===0) return
 }
 },
 // 检查是否还没胜利
 checkNoWin() {
 let cutImgs=this.instance.cutImgs
 let trueTransforms=this.config.trueTransforms
 for (let i=0, j=this.instance.cutImgs.length; i < j; i++) {
 if (cutImgs[i].style.transform !==trueTransforms[i]) return true
 }
 },
 // 清空已有数据
 reset() {
 let resetParam=this.resetParam
 this.config=this.deepCopy(resetParam.config)
 this.instance=this.deepCopy(resetParam.instance)
 this.tool=this.deepCopy(resetParam.tool)
 download.style.display='none'
 },
 deepCopy(obj) {
 return JSON.parse(JSON.stringify(obj))
 },
 // 打开图片
 openImage() {
 window.open(this.config.imgUrl)
 },
 // 重置时候的初始化参数
 resetParam: {
 // 配置
 config: {
 level: 3,
 cutImgsCountArray: [],
 trueTransforms: [],
 imgCutStyle: {},
 imgUrl: '',
 },
 // 实例
 instance: {
 // 所有图片的实例
 cutImgs: [],
 },
 // 记录工具
 tool: {
 currentIndex: -1
 },
 }
}
Game.restart()

js 就麻烦许多许多了,逻辑和功能匹配,还要用到一些冷门的知识,比如 styleSheets 相关知识,一直用框架,都快忘光了。

说起来简单,就是把前后选中的容器进行 transform 的替换。但是需要注意是基础的业务逻辑:

  1. 第一次和下一次点击的是同一个,那么是要取消选中。
  2. 交换后,需要两个都取消选中。
  3. 重置游戏需要情况上一轮的样式,重新排版。
  4. 游戏过关的业务逻辑。
  5. 游戏难易度配置。
  6. 过关奖励,嘿嘿嘿。
  7. 等等等等。

注意里面有个生成可还原的排序,具体见我之前文章:逆序数,拼图游戏必备知识

具体基本逻辑都在代码里面,相关注释也有加上,喜欢喜欢的小伙伴仔细看看,试试手,练一练。

在这里就不长篇赘述了。

祝你玩的开心。


欢迎关注,如有需要 Web,App,小程序,请留言联系。

.话不多,先瞅效果:

又在别的地方嫖到了这个效果研究了亿下下,制作过程如下(超详细):

二.实现过程(源码在最后):

1.定义canvas标签:

 <canvas id="canvas"></canvas>

2.基本css样式:

#canvas{
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%,-50%);
            box-shadow:  0 0 10px rgb(150, 150, 150);     
        }

position: absolute; 绝对定位。
top: 50%;
left: 50%;
transform: translate(-50%,-50%); 居中。
box-shadow: 0 0 10px rgb(150, 150, 150); 阴影。

3.开始js部分,获取标签:

var canvas=document.querySelector("#canvas");
var ctx=canvas.getContext('2d');            

4.定义基本变量:

        //画布宽
        var wide=600;
        //画布高
        var high=600;
        // 变量,判断一次渲染中只识别按键一次
        var kd=0;
        //当前分数
        var fraction=0;
        //速度,就是执行定时器的时间参数
        var speed=250;
        // 蛇的初始颜色 红色
        var yanse=`red`;
        // 蛇数组,组成蛇的每一个方块
        var snake=[];
        // 食物数组
        var food={};
        // 蛇的移动方向,x轴:1为向右,-1为向左;y轴:1为向下,-1为向上 。不能斜着走,所以0为某轴无方向。
        var diretion={
            x:-1,
            y:0
        }
        // 给画布宽高赋值 打算画一个长宽都是30个20px的方块画布
        canvas.width=wide;
        canvas.height=high;

5. 初始化:

function chushi(){            
        //蛇初始长度为3个方块,位置如下(这个随意)
            for(let i=0;i<3;i++){
                snake.push({
                    x: i+10,
                    y: 10
                })
            }
            // 给食物一个随机位置和随机颜色
            food={
                x: parseInt(Math.random()*30),
                y: parseInt(Math.random()*30),
                color:`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255})`
            }
        }

6. 绘制图形:

  // 绘制图形
        function draw(){
            // 绘制显示当前分数的文字
            ctx.fillStyle='rgba(255,255,255,0.5)';
                ctx.font="50px 仿宋";
                ctx.textAlign='center'; 
                ctx.fillText("你的分数为:"+fraction+" 分",300,300);

             // 绘制方格,长宽都是30个,都是19px*19px的方格   
            for(let i=0;i<30;i++){
                for(let j=0;j<30;j++){
                    ctx.fillStyle='rgba(255, 255, 255,.3)';
                    ctx.fillRect(i*20,j*20,19,19);
                }
            }
              // 绘制蛇
            for(let i=0;i<snake.length;i++){
                temp=snake[i];
                ctx.fillStyle=yanse;
                ctx.fillRect(temp.x*20,temp.y*20,19,19);
                // 判断蛇头(第一个方块)是否与身体某个方块重合 ,就是头撞到身体
                if(temp.x==snake[0].x&&temp.y==snake[0].y&&i!=0){
                    // 游戏结束,重新给初始化
                    alert('游戏结束~点击确认再来一次~'); 
                    fraction=0;
                    snake.length=0;  
                    chushi();
                    
                }
            }
               // 绘制食物,绘制一个圆形
                ctx.beginPath();
                ctx.fillStyle=food.color;
                ctx.arc(food.x*20+9.5,food.y*20+9.5,7,0,Math.PI*2,false);
                ctx.stroke();
                ctx.fill();
                ctx.closePath();
      
               // 给蛇头绘制一个字符,☆ ,好区分头尾 ,也可省略
                ctx.fillStyle='yellow';
                ctx.font="15px 仿宋";
                ctx.textAlign="start";
                ctx.fillText("☆",snake[0].x*20+2,snake[0].y*20+14.5);
        }

7.更新位置:

 //更新
        function update(){
            // 建一个对象head,这个为蛇的新头,通过绘制新头,去掉尾部实现移动效果
            var head={};
            //判断蛇头是否遇到边界,到边界则在另一边重新绘制 x轴
            switch (snake[0].x+diretion.x){
                case -1: head.x=29;break;
                case 30: head.x=0;break;
                // 没到边界则为当前位置加方向
                default:  head.x=snake[0].x+diretion.x;
            }
           //判断蛇头是否遇到边界,到边界则在另一边重新绘制 y轴
            switch (snake[0].y+diretion.y){
                case -1: head.y=29;break;
                case 30: head.y=0;break;
                // 没到边界则为当前位置加方向
                default:  head.y=snake[0].y+diretion.y;
            }
            // 判断新蛇头是否与食物重合,就是吃到食物
           if(head.x==food.x&&head.y==food.y){
               //蛇的颜色为吃到食物的颜色
            yanse=food.color;
             // 重新给食物初始化
            food={
                x: parseInt(Math.random()*30),
                y: parseInt(Math.random()*30),
                color:`rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255})`
            }
            //在蛇尾添加一节
            let temp=snake[length-1];
            snake.push(temp);
            fraction+=1;
            // 吃完食物速度加快
            if(speed>80){
                //定时器间隔减10
                speed=speed-10;
                // 清除原来定时器,重新绘制
                clearInterval(time);
                 time=setInterval(function () {
                    kd=0;
                    ctx.clearRect(0, 0, wide, high);
                    update();
                    draw();

                }, speed);
            
            }
           
           }
            //添加新头
            snake.splice(0,0,head);
            //去掉尾部
            snake.pop();
                     
        }

8.判断点击键盘事件:

 //判断点击事件 
        document.addEventListener('keydown', event=>{
              switch (event.keyCode){
                  // 按了向上键
                  case 38:
                      // 判断当前不是向下移动与还没按过键,否则蛇会重叠
                      if(diretion.y!=1&&kd==0){
                          // 重新给移动方向赋值
                        diretion.x=0;
                        diretion.y=-1;
                        kd=1;
                      }
                      break;
                      // 下面以此类推一样的原理
                  case 39:
                      if(diretion.x!=-1&&kd==0){
                        diretion.x=1;
                        diretion.y=0;
                        kd=1;
                      }
                      break;
                  case 40:
                      if(diretion.y!=-1&&kd==0){                     
                      diretion.x=0;
                      diretion.y=1;
                      kd=1;
                      }
                      break; 
                  case 37:
                      if(diretion.x!=1&&kd==0){
                        diretion.x=-1;
                        diretion.y=0;
                        kd=1;
                      }
                      break;        

              }
        })

9.设置定时器,开始动画:

 chushi();    
            var time=setInterval(function(){
            kd=0;
            ctx.clearRect(0,0,wide,high);
            update();
            draw();      
        
        },speed);

三.完整代码: