javascript运动系列第九篇——碰撞运动

眼前的语

  碰撞可以分成碰壁和互碰两种植形式,上篇介绍了碰壁运动,本文将从浅入雅地介绍碰撞运动的互碰形式

 

游戏机制是游玩剥离美学、技术、故事设定之后最好纯粹的一些,也是耍的实在内核。虽然并没包括万物的完备理论来解构游戏机制,但模糊地吧,还是得以分为六独重要有:1)空间,2)对象、属性和状态,3)行为,4)规则,5)技能,6)偶然性。

碰撞检测

  对于互碰形式的冲击运动以来,首先使缓解的凡碰撞检测。对于矩形元素的碰撞检测前的博文已经详尽介绍过,下面要介绍圆形元素的碰撞检测

  矩形元素的碰撞检测利用九宫格分析法,而环元素的碰撞检测则略好多,判断两独圆圈元素的半径的与是否超出两单圆形元素的圆心点坐标中的离即可

葡京娱乐十大排名 1

  由示意图可知,元素一之圆心位置为(x1,y1),半径为r1,元素二的圆心位置吗(x2,y2),半径为r2

  两独因素圆心之间的距离len = Math.sqrt((x1-x2)*(x1-x2) +
(y1-y2)*(y1-y2))

  当len<= r1+r2时常,说明两独圆形元素来碰撞

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test1" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;border-radius: 50%;"></div>
<div id="test2" style="height: 200px;width: 200px;background:orange;position:absolute;top:150px;left:150px;border-radius: 50%;"></div>
<script>
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
} 
function bump(obj,objOther,bgColor){

    /***被碰元素***/
    var r1 = obj.offsetWidth/2;
    var x1 = parseFloat(getCSS(obj,'left')) + r1;
    var y1 = parseFloat(getCSS(obj,'top')) + r1;
    /**侵入元素**/
    var r2 = objOther.offsetWidth/2;
    var x2 = parseFloat(getCSS(objOther,'left')) + r2;
    var y2 = parseFloat(getCSS(objOther,'top')) + r2;
    //碰撞检测
    var len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

    if(len <= r1 + r2){
        obj.style.backgroundColor = 'red';
    }else{
        obj.style.backgroundColor = bgColor;
    }
}


function drag(obj){
    obj.onmousedown = function(e){
        e = e || event;
        //提升当前元素的层级
        obj.style.zIndex = '1';
        //获取元素距离定位父级的x轴及y轴距离
        var x0 = this.offsetLeft;
        var y0 = this.offsetTop;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        var x1 = e.clientX;
        var y1 = e.clientY;
        //鼠标按下时,获得此时的页面区域
        var L0 = 0;
        var R0 = document.documentElement.clientWidth;
        var T0 = 0;
        var B0 = document.documentElement.clientHeight;
        //鼠标按下时,获得此时的元素宽高
        var EH = obj.offsetHeight;
        var EW = obj.offsetWidth;
        document.onmousemove = function(e){
            e = e || event;
            //获取此时鼠标距离视口左上角的x轴及y轴距离
            x2 = e.clientX;
            y2 = e.clientY;    
            //计算此时元素应该距离视口左上角的x轴及y轴距离
            var X = x0 + (x2 - x1);
            var Y = y0 + (y2 - y1);
            /******范围限定*******/
            //获取鼠标移动时元素四边的瞬时值
            var L = X;
            var R = X + EW;
            var T = Y;
            var B = Y + EH;
            //在将X和Y赋值给left和top之前,进行范围限定
            //只有在范围内时,才进行相应的移动
            //如果脱离左侧范围,则left置L0
            if(L < L0){X = L0;}
            //如果脱离右侧范围,则left置为R0
            if(R > R0){X = R0 - EW;}
            //如果脱离上侧范围,则top置T0
            if(T < T0){Y = T0;}
            //如果脱离下侧范围,则top置为B0
            if(B > B0){Y = B0 - EH;}
            obj.style.left = X + 'px';
            obj.style.top = Y + 'px';
            //运行碰撞检测函数
            bump(test2,test1,'orange')
        }
        document.onmouseup = function(e){
            //降低当前元素的层级
            obj.style.zIndex = '0';
            //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
            document.onmousemove = null;
            //释放全局捕获
            if(obj.releaseCapture){
                obj.releaseCapture();
            }
        }
        //阻止默认行为
        return false;
        //IE8-浏览器阻止默认行为
        if(obj.setCapture){
            obj.setCapture();
        }
    }    
}
drag(test1);
drag(test2);
</script>      
</body>
</html>

机制 #1
空间
:所有的游玩还发空中,但并没有硬性的规定来讲述这些游戏空间的界限。

无损碰撞

  假而两只要素的相撞,对素的进度并无发生损耗,而只是是改元素速度方向

  假设元素一每当同素二碰撞前的刹那速度是v,将该速度分解为平于碰撞方向的速度v1和直于碰撞方向的速v2

  碰撞产生后,碰撞方向的进度v1变成了反向的v1

  将反向的v1分解至水平方向v1x和直方向v1y

  将直于碰撞方向的速v2分解至水平方向v2x和直方向v2y

    水平方向的速度vx = v2x - v1x
    垂直方向的速度vy = v2y - v1y

  元素二的进度分解形式与素一近乎,就不再赘言

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<button id="btn1">开始运动</button>
<button id="reset">还原</button>
<div id="test1" style="height: 150px;width: 150px;background:pink;position:absolute;top:50px;left:50px;border-radius: 50%;"></div>
<div id="test2" style="height: 150px;width: 150px;background:orange;position:absolute;top:250px;left:250px;border-radius: 50%;"></div>
<script>
//声明元素的步长值
//步长值默认值为[-25,-20,-15,-10,-5,0,5,10,15,20]中的一个随机数
test1.stepX =  5*Math.floor(Math.random() * 10 - 5);
test1.stepY =  5*Math.floor(Math.random() * 10 - 5);
test2.stepX =  5*Math.floor(Math.random() * 10 - 5);
test2.stepY =  5*Math.floor(Math.random() * 10 - 5);
btn1.onclick = function(){
    collisionMove({
        obj:test1
    })
    collisionMove({
        obj:test2
    })
}
reset.onclick = function(){
    history.go();
}
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //声明x、y轴的当前值
    var curX,curY;
    //声明x、y轴方向
    var dirX = json.dirX;
    var dirY = json.dirY;
    dirX = obj.stepX > 0 ? '+' : '-';
    dirY = obj.stepY > 0 ? '+' : '-';
    //声明offset宽高
    var offsetWidth = obj.offsetWidth;
    var offsetHeight = obj.offsetHeight;
    //声明元素活动区域宽高
    var activeWidth = json.activeWidth;
    var activeHeight = json.activeHeight;
    //元素获取区域宽高默认值为可视区域宽高
    activeWidth = Number(activeWidth) || document.documentElement.clientWidth;
    activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
    //声明left、top样式值
    var left,top;
    //清除定时器
    if(obj.timer){return;}
     //开启定时器
    obj.timer = setInterval(function(){
        //获取x、y轴的当前值
        curX = parseFloat(getCSS(obj,'left'));
        curY = parseFloat(getCSS(obj,'top'));
        bump(test1,test2);
        //更新left、top值
        left = curX + obj.stepX;
        top = curY + obj.stepY;
        //右侧碰壁前一刻,步长大于剩余距离,且元素向右运动时
        if((left > activeWidth - offsetWidth) && (dirX == '+')){
            left = activeWidth - offsetWidth;
        }
        //左侧碰壁前一刻,步长大于剩余距离,且元素向左运动时
        if((Math.abs(obj.stepX) > curX) && (dirX == '-')){
            left = curX;
        }
        //下侧碰壁前一刻,步长大于剩余距离,且元素向下运动时
        if((top > activeHeight - offsetHeight) && (dirY == '+')){
            top = activeHeight - offsetHeight;
        }
        //上侧碰壁前一刻,步长大于剩余距离,且元素向上运动时
        if((Math.abs(obj.stepY) > curY) && (dirY == '-')){
            top = curY;
        }
        obj.style.left= left + 'px';
        obj.style.top = top + 'px';
        //左侧或右侧碰撞瞬间
        if(left == activeWidth - offsetWidth || left == curX){
            obj.stepX = -obj.stepX;
        }
        //上侧或下侧碰撞瞬间
        if(top == activeHeight - offsetHeight || top == curY){
            obj.stepY = -obj.stepY;
        }
        //更新运动方向
        dirX = obj.stepX > 0 ? '+' : '-';
        dirY = obj.stepY > 0 ? '+' : '-';
    },20);            
}

function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
} 
//碰撞检测函数
function bump(obj,objOther){
    /***动态元素***/
    obj.r = obj.offsetWidth/2;
    obj.x0 = parseFloat(getCSS(obj,'left')) + obj.r;
    obj.y0 = parseFloat(getCSS(obj,'top')) + obj.r;
    /**静态元素**/
    objOther.r = objOther.offsetWidth/2;
    objOther.x0 = parseFloat(getCSS(objOther,'left')) + objOther.r;
    objOther.y0 = parseFloat(getCSS(objOther,'top')) + objOther.r;
    //圆心之间的距离
    var len = Math.sqrt((obj.x0-objOther.x0)*(obj.x0-objOther.x0) + (obj.y0-objOther.y0)*(obj.y0-objOther.y0));
    //发生碰撞
    if(len <= obj.r + objOther.r){
    //碰撞方向与水平负方向的夹角a 
        var a = Math.atan(Math.abs((obj.y0-objOther.y0)/(obj.x0-objOther.x0)));
        stepChange(test1,test2,a);
        stepChange(test2,test1,a);
    }
}
//碰撞时,步长变化函数
function stepChange(obj,objOther,a){
    //步长合并
    obj.step = Math.sqrt(obj.stepX*obj.stepX + obj.stepY*obj.stepY);
    //假设总步长方向与x轴方向的夹角为b
    obj.b = Math.atan(Math.abs(obj.stepY/obj.stepX));
    //假设总步长方向与碰撞方向的夹角为c
    obj.c = Math.abs(a - obj.b);
    //步长分解
    //碰撞方向
    obj.step1 = obj.step*Math.cos(obj.c);
    //垂直方向
    obj.step2 = obj.step*Math.sin(obj.c);
    //按照运动元素(侵入元素)的起始运动方向对步长进行重新分解
    //左上
    if(obj.x0 <= objOther.x0 && obj.y0 <= objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //左下
    if(obj.x0 < objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a) 
    }
    //右上
    if(obj.x0 > objOther.x0 && obj.y0 < objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //右下
    if(obj.x0 > objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
    }
}
</script>      
</body>
</html>
  • 她可是离散的亚维空间,比如多数棋盘,围棋、象棋、五子棋,每一个得以放棋的格子在拓扑学上虽是一个零维空间单元,有些单元以及单元中存在正在连日来来发表某种逻辑规则。
  • 它为堪是连的次维空间,比如台球桌,比如超级马里奥,甚至是三维空间,比如足球的球门,反恐精英等。
  • 还有嵌套空间,比如小奇幻游戏当中的洞穴,城堡等,玩家可进去这些和外表空间了分隔的独门空间。虽然当物理模型上讲话是不符现实的,但她恰恰吻合我们对空中认知的心理模型。这种嵌套空间会挺好地将纷繁的社会风气因此同样栽简单的点子呈现。
  • 还有特别的零维空间,比如用二十单应答「是」或「否」的题材猜出对方想的物的一日游中,游戏空间发出在对话中,还有玩家大脑里想象的逻辑树。

发出损伤碰撞

  匀速有误碰撞是当无损碰撞的根底及,每次打都发肯定的速损耗,在冲击或碰壁的一瞬乘机以吃因子即可

  变速有贬损碰撞类似于击打台球,元素运动时,速度就径直于减少,碰撞或碰壁时,除了速度方向改变他,速度吗不无损耗,当速度减小到0时,停止活动。由于代码相似,就不再重复,源码见这

机制 #2
靶、属性和状态
:对象是游玩空间里活动之实业,比如一个跑车游戏里,赛车是一个对象,赛车拥有时速,加速度之类的性,而实际到数值的时段,就是状态。

当设计 AI
角色的时刻写一个态机有时候会格外有因此,比如吃豆类人之「幽灵」角色的状态机:

各一个圈代表幽灵的状态,双线圆圈代表初始状态,箭头代表可能的状态转换,箭头上面的文字代表状态转换条件。这个状态机仍然是简化了底版,比如在「追击吃豆人」这同样步着,还有「搜索吃豆人」到「尾随吃豆人」的子状态。

娱的习性与状态并不一定需要公开,对有些游戏仍扑克游戏和微卡牌游戏,通常玩家手中的牌只有玩家自己清楚。游戏性的基本点其实在猜测对手的手牌。

机制 #3
行为
:行为就是「玩家会举行呀」。有一定量种植办法,一种植是玩家可以开的主干「操作」,第二种植是玩家的这操作造成的「结果」。比如以围棋当中,玩家的操作就是于19×19底棋盘中的空位落一子。但以此操作的结果就是颇的多:提一个子,占一片地,做一个双眼,威胁对方,弃子争先,等等一样密密麻麻策略性的结果。

一个好之打通常会持有一个低之「操作」/「结果」比例,即少量底操作会生有大量之生成。玩家当天然地创造有有国策时也还要于也他们协调创建经验。这里出五独提醒,能支援您建一个「自发游戏」:

  • 加上更多的操作,增加操作中发生意义,有变动之交互。比如「走」「跑」「跳」「射击」比单独的「走」拥有更多之操作间交互的可能,也会见进一步有意思。但若小心过分臃肿的操作并无可知也一日游带来重新好之心得,注意「操作」/「结果」的百分比。
  • 操作以大量对象上,比如您可「射击」,不但可发射怪物,还足以发门把手,玻璃窗,吊灯,轮胎。
  • 对象可以经过多种主意及,这等同漫长要和方面一样漫漫配合,比如您照一个怪,你可发怪物把从大,也可射击门把手逃出十分东西的支配范围,也堪打吊灯把死东西压住等。当然,这样的规划会给游玩平衡性受到挑战,如果玩家拥有了同栽大庭广众优势的选取,那么玩家可能总会坚持那种选择。
  • 大量的预兆对象,主对像就会生操作的靶子,比如围棋的棋,围棋的魅力少不了大量的棋子,事实上,对于围棋来说,玩家其实有极的棋子,因为当棋子用完之前游戏一定会结束。
  • 操作带来的游戏空间的改动,还是当围棋中,每一样发棋子于棋盘上同任何棋子一起所形成的「势」会天天改变玩家的策略,对围棋来说,有的规则还规定棋盘上未容许同一的棋形再次出现。

机制 #4 规则:规则约束了玩家的作为,也拉动了玩家的靶子,下图是
David Parlett 的平整分析。

David Parlett 的规则分析

  • 操作规则:玩家在打闹中需举行啊?当一个玩家了解操作规则后,就足以起来玩游戏了。
  • 主干规则:我之领悟是,玩家的目标,以及状态变化之同种数学表现,通常玩家并不知道这些,设计师也十分少正式地用挑大梁规则全集文档化。
  • 作为规则:行为规则是富含的,玩家中默认的平整,比如观棋不晓,落子无悔等。
  • 封面规则:书面规则就是是玩家用懂得的游戏规则的封皮文字版,通常只有出那个少数的人头见面看这些文字。大多数玩家通过他人之讲述与介绍,或者视频游戏受之只是交互教程学会游戏。设计师应在游玩就时会轻轻松松地报玩家如何去玩,而无是比如说程序员一样告诉对方:Read
    The F*cking Mannual.
  • 律:只有以生严肃的,竞技性比赛之场地才见面出这样的条条框框,这些规则通常为叫做「竞标赛规则」。比如三局两胜,淘汰赛规则等。法规是以游戏规则之外确保平衡与正义的一对规则。
  • 专业规则:正式规则是法律和书面规则结合在一起的条条框框,有时候法规最后见面同化到书面规则中,比如「五子棋」当中的禁手规则。
  • 提议规则:建议规则并无算是规则,只是为让游玩家玩的重复好的组成部分提示。比如围棋里面的起始定式。
  • 小众规则:这个规则并没于 Parlett
    明确地描写出来,但是玩家在打的过程中,可能会见无满意游戏被之一点设定,或者想平衡玩家技术差距要活动开展的变更。比如围棋中的让子。

除开以上这些,规则中最要之有些是目标,游戏目标有三只性状,1)具体到玩家们能够清晰地亮并复述出她们之对象。2)玩家用看他俩产生会达到目标。3)完成目标后底奖。

机制 #5 技能:大多数玩耍需要玩家操纵技术,这些技巧可大约为分成3类:

  • 人技能:包括力量,灵活度,协调性和耐久力,经常是体育游戏之要组成部分,有些视频游戏也会见要求手眼配合能力。
  • 脑子技能:这些技巧包括记忆力,观察力,解密能力。有些人或者会见避开一些脑筋游戏,但大多数诙谐的嬉戏都得脑力来举行决定。
  • 社交技能:包括洞察对方的想法,蒙骗对手和和队友合作等。

上述技术都是玩家的忠实技能,除此之外还有虚拟技术,比如戏角色的品,招式等。在玩家真实技能没有另外提升的前提下,提升游戏角色的虚拟技术,可以给玩家带来好的力量感受。当然,如果滥用的话,会吃丁感觉到异常假。

机制 #6
偶然性
:最后是机制以跟另五单机制相互作用。这吗是平慢性游戏受之中心部分,不确定性意味着惊喜,是意之神秘要素。

设计师最好会清楚一些主干的排列组合,概率论,期望值算法以及计算机帮忙套算法。如果没有道吧,至少要知道哪个知道以可助到你。

而是设计师不是数学家,设计师除了关心事件之实在概率,同时还要体贴感知概率。所谓感知概率是众人唯恐在心里中强估计或低估一些概率。比如当死亡之概率会受低估,而无当然死亡的几率则会吃强估计。此外,还要考虑风险厌恶型和风险偏好性的人头之抉择并无了依据期望值来挑选。尤其是若并无告诉玩家具体的数值,玩家自己尝尝时无感觉选择的早晚。

对此玩家来说,技能与几率是纠缠在联名的。比如

  • 评估概率对玩家来说是一律种技术。
  • 评估对手能力,做出一些假象,比如表现得被对方认为你老强,从而阻碍他们用风险的行路,或者变现得叫挑战者认为你特别死,从而诱使他们轻敌或冒险。
  • 预计和决定纯随机是一律种想象力,玩家见面顺便地摸模式,寻找原因以及呈现之间的关联,即使是纯随机事件。作为同号称设计师,应该了解并采取玩家的这种心理,让玩家当自己当磨砺并增强了技术从而获取更老之趣。

总结

lens #21 功能空间:用这些题目想游戏机制被诚的长空:

  • 这个游戏之空间是连连的要么离散的?
  • 空间有多少维度?
  • 空中的分界在啊?
  • 发出没有子空间,子空间是怎连接起来的?
  • 得抽象,简化建模游戏空间为?

lens #22
状态
:玩游戏是决策制定的经过,决策制定需要信息,设计者需要控制谁会明白这些消息。询问好如下问题:

  • 游戏受之目标还是什么?
  • 这些目标来什么样性?
  • 每个属性之或状态是啊?以及这些状态里转移的规则是呀?
  • 哎呀状态是勿待玩家知道之?
  • 嗬状态是单纯来一对玩家知道之?
  • 什么状态是所有玩家都懂的?
  • 改状态的公然程度会以少数方面提升游戏吧?

lens #23 自发游戏:让游戏所有更多之成形:

  • 玩家所有小种操作?
  • 每个操作可以同稍种对象,多少只对象开展交互?
  • 玩家所有小种方法来实现目标?
  • 玩家拥有小目标?
  • 操作及对象期间的相互能否带来娱乐策略/空间的变更?

lens #24
行为
:思考玩家会举行啊,设计师需要小心地计划作为,要么创造有令人惊喜之「自发游戏」,要么就算是不用希望的乏味游戏。询问自己如下问题:

  • 玩被之操作行为发出哪,结果表现可能出什么样?
  • 若还想看到什么样结果,如何改变操作为这些结果变得中?
  • 若看玩家还想看到怎样结果,是否行得通,如何转操作为这些结果变得中?
  • 结果表现与操作行为之比重是否让而称心?

lens #25 目标:游戏的靶子应该适当且平衡,询问自己如下问题:

  • 游玩的最终目标是什么?
  • 玩家们清楚地知道这个目标吧?
  • 除却最终目标外,还有什么阶段性目标?
  • 阶段性目标是否相关联?
  • 阶段性目标是否足够具体,可达成,且发出奖励?
  • 玩家们来会决定他们好之目标呢?

lens #26
规则
:设计师需要持续地测试和改写规则,直到游戏最终就,询问好如下问题:

  • 戏有安操作规则。
  • 陪在游戏之进步,是否形成了一部分「法规」或「内部平整」呢?这些规则是否该被直接成至打内?
  • 在娱乐中有子游戏啊?子游戏是否给戏变得重复好打了呢?能否减少或长?
  • 是否用裁判,或执法者。
  • 这些规则是否好理解,如何改善?

lens #27
技能
:审视要求玩家操纵的技能,利用得当的挑战及虚构技术让练习技巧变成一宗充满生趣之作业,询问好如下问题:

  • 逛戏都务求玩家操纵什么技艺?
  • 游玩被是不是少某种技能项目?
  • 什么技能是着重技术?
  • 这些技巧可创造有我所企盼之游戏体验吧?
  • 是否有些玩家对这些技巧的支配会强烈好了任何玩家,如何平衡?
  • 玩家是否通过练习来提升他们的技巧水平?
  • 当时款打是否需要某种程度的技能水平?

lens #28
期望值
:思考游戏不同事件葡京娱乐十大排名之出几带领,期望值是平衡游戏最好之家伙。询问好如下问题:

  • 这些事件的出几引领是聊?
  • 玩家感知的几乎率领又是有点?
  • 坏事件负有什么样的想望值?能否让量化?
  • 咱对玩家认知的企值感到满意为?这些愿意值为玩家感到有趣了也?玩家收到的赏或者处以是否方便?

lens #29
概率
:适量运用风险与随机性,能调动和戏为游玩还美味,同时不要局限为数字概率,所有的茫然事物中还足以涵盖偶然性。询问好如下问题:

  • 游玩被如何有是当真自由的?哪些有是为人口感到自由的?
  • 这些随意的局部是否也玩乐的趣味性做了孝敬?
  • 通过调整概率分布能否升迁游戏的趣味性?
  • 玩家是否为了好玩的体会而负担了风险?
  • 几乎率领及技巧中的发无发出纠缠,是否生原是自由的素让玩家感到更像是技术的操练?
  • 是否拥有方法让技艺的练习更像是比照机元素?

立马首稿子是自读 Jesse Schell 的 The Art of Game
Design
的笔记和感悟,本书也起中文译本,名字被全景探秘游戏设计方法。接下来的几乎上,我会陆续揭晓后续的章笔记。


且看就了,留个说,点亮那个 ♡ 让自己开玩笑一下吧~~\_