交互媒体技术应用——2D横版冒险游戏初尝试

2023-11-21 20:20

本文主要是介绍交互媒体技术应用——2D横版冒险游戏初尝试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

2D横版冒险游戏——模拟人生《Just Die》

  • 想法
  • 背景
    • 使用工具
    • 参考资料
  • 关键技术
    • 小球的运动
    • 碰撞检测
    • 粒子系统
    • 优化算法
      • 粒子系统的预处理
      • 从预处理的粒子系统中获取需要的子系统
    • 其他
  • 后记
  • 附录

想法

最早是在B站看到一个视频,介绍了一个模拟人生的横版游戏,玩家需要操控游戏中的角色,根据不同的关卡做出不同的举动,来完成他的一生。简单但是很有深意,让我思考了很久,并在心里埋下了这个想法。这是一个就我个人而言代入感十分强烈的小Demo,包含我对这20年成长的一些思考,可能不有趣,而且由于代码能力不足,有一些糟糕的bug,导致游戏可玩性不强,以后应该会继续做,做出我满意的样子来。

背景

使用工具

processing

参考资料

《代码本色 The Nature of Code》 —— Daniel Shiffman
在这里插入图片描述
本Demo中参考的内容主要包含本书的前四章,向量、力、粒子系统等。

关键技术

小球的运动

将小球定义为包含位置(location)、速度(velocity)以及加速度(acceleration)的对象,根据加速度更新速度,根据速度更新位置,来实现小球的运动效果。这三个变量都存储在Processing自带的向量PVector中。

class people extends Collider {PVector velocity;  //速度PVector acceleration;  //加速度float topspeed=2; //最大速度float mass=10;  //质量float c=0.001;//摩擦力系数people() {location=new PVector(10, height-10);velocity=new PVector(0, 0);acceleration=new PVector(0, 0);R=5.0;}people(PVector location_, PVector velocity_, PVector acceleration_, float R_) {location=location_.get();velocity=velocity_.get();acceleration=acceleration_.get();R=R_;}}

在Update中更新变量,并在display绘制实时图像:

  void update() {applygravity();velocity.add(acceleration);location.add(velocity);velocity.limit(topspeed);acceleration=new PVector(0, 0);}void display() {fill(0);ellipse(location.x, location.y, 2*R, 2*R);}

这里面还包含了对力的应用addforce();我们知道,速度决定位置的变化,加速度决定速度的变化,而加速度来源于力,因此当我们想要改变一个物体的运动状态时,只需要给他加上一个力即可:

  void applyForce(PVector force) {PVector f=force.get();f.div(mass);acceleration.add(f);}

还可以细分为许多不同类型的力,如重力、摩擦力、弹力、万有引力等:

  void applyfriction() {PVector velocity_=velocity;velocity_.mult(-1);velocity_.normalize();PVector friction=velocity_.mult(c);applyForce(friction);}
  void applygravity() {PVector gravity=new PVector(0, 0.2);applyForce(gravity);}

(本Demo中虽然有计算摩擦力的函数,但是由于一些不知名Bug以及时间有限没用实际应用摩擦力,这里只做展示)

碰撞检测

其实前人已经做了十分完善的、包含碰撞的物理库函数Box2D(该库在代码本色的第五章中有详细介绍),但是由于作业要求用到三章的知识,所以我没用调用物理库而是自己实现了一个简陋的碰撞检测机制:

  Collederflag isCollision(Collider p) {float Mindis=R+p.R;if (abs(location.x-p.location.x)<=Mindis&&abs(location.y-p.location.y)<=Mindis) {if (location.x>=p.location.x) {//左侧撞击flag.horiflag=-1;} else if (location.x<p.location.x) {//右侧撞击flag.horiflag=1;}if (location.y<=p.location.y) {//下侧撞击flag.verflag=1; //   println("上方");} else if (location.y>p.location.y) {//上侧撞击flag.verflag=-1;//  println("下方");}return flag;} else {return flag;}}

原理大概就是根据计算每个物体的中心间的距离,根据这个距离与半径之间的关系来判断物体是否发生碰撞。在该Demo中根据两个碰撞物体的不同还有多个类似的碰撞检测函数,这里就不放了(其实只是因为本人代码能力不足,导致代码有些乱,也没用运用接口)。

粒子系统

在这里插入图片描述
在本Demo中存在各种不同的对象,我们将他们存储在粒子系统中。比如图片中作为边界和障碍物的砖块:
我们将他们定义为rectframe类,并在ArrayLIst frames中存储他们:

class rectframe extends frame {rectframe(PVector location_, float R_) {location=location_.get();R=R_;}void display() {rectMode(CENTER); stroke(255);strokeWeight(1);fill(0);rect(location.x, location.y, 2*R, 2*R);}
}
ArrayList<frame> frames = new ArrayList<frame>();

类似的还有图中的发射装置、发射出的子弹等,都是存储在粒子系统中的,以便于我们对一类对象进行相同的操作。

ArrayList<shootframe> shootframes = new ArrayList<shootframe>();
ArrayList<record> records = new ArrayList<record>();
ArrayList <moveblock> moveblocks=new ArrayList<moveblock>();

遍历粒子系统中的所有对象并在画面上绘制:

      for (frame f : frames) {f.display();}

优化算法

为了给小球施加力,我们需要判断小球是否与其他物体发生了碰撞,但是如果每帧都让小球对所有物体进行一次碰撞检测,这显然是不现实并且十分浪费的。所以我们需要进行一些优化,首先我们对所有数据预处理。

粒子系统的预处理

我们知道粒子系统就像一个能存储结构体的数组。而将数据存入粒子系统也很简单:

    List.add(f);

其中List 是ArrayList类型的对象,而f就是你要存储的数据。
但是只是存储是不够的,为了减少计算量,在每次存储新元素时,我们按照位置来插入元素,这里运用了之前在数据结构还是哪门课上学习的思路。这就好比现在给你一个乱序的数组[1,8,2,5,3,6,0,2],让你找一个大于3小于6的数,那么我们必须对每个元素进行判断,其是否大于3?其是否小于6?而如果给你一个排好序的数组[0,1,2,2,3,5,6,8],那么我们只需要从头开始判断,当我们找到数组中第一个大于3的数的位置,再从这个位置开始,找到第一个大于6的数的位置,这两个位置之间的数,就是我们需要的。但是每帧都对所有粒子系统排序显然效率也是很低的,所以我们只在生成对象,插入粒子系统的过程中,按照其x的坐标插入,就可以插入得到一个有序的粒子系统:

//---------------用于将新生成的frame对象插入ArrayList中
void insert(ArrayList List, frame f, int min, int max) {if (max==0) {//初始化List.add(f);return;}int mid=(max+min)/2;frame p=(frame)List.get(mid);//println(p.location.x);//println("mid:"+mid);//println("min:"+min);//println("max:"+max);frame pmin=(frame)List.get(min);frame pmax=(frame)List.get(max-1);//println("pmin:"+pmin.location.x);//println("pmax:"+pmax.location.x);if (f.location.x<pmin.location.x) {List.add(0, f);return;} else if (f.location.x>pmax.location.x) {List.add(max, f);return;} else if (f.location.x<=p.location.x) {frame pmid=(frame)List.get(mid-1);if (f.location.x>=pmid.location.x) {List.add(mid, f);return;}max=mid;insert(List, f, min, max);} else if (f.location.x>=p.location.x) {frame pmid=(frame)List.get(mid+1);if (f.location.x<=pmid.location.x) {List.add(mid+1, f);return;}min=mid;insert(List, f, min, max);}
}

从预处理的粒子系统中获取需要的子系统

然后当我们需要进行碰撞检测时,我们只需要根据阈值,从粒子系统中取出可能进行碰撞的子系统,对子系统中的粒子进行碰撞检测即可:

// 遍历数组,只取出ArrayList中有可能与YOU进行碰撞的对象进行碰撞检测
ArrayList<frame> findArrayList(ArrayList<frame> frames, Collider p) {ArrayList<frame> newframes=new ArrayList<frame>();float x=p.location.x;float R=p.R;float Up=x+maxR+R;float Down=x-maxR-R;//println("x:"+x);//  println("Up:"+Up);//    println("Down:"+Down);for (frame f : frames) {if (f.location.x>=Down&&f.location.x<=Up) {newframes.add(f);}}return newframes;
}

其他

其他还包括许多诸如开始界面的绘制、死亡提示、电影谢幕效果等,这里不放太多代码,具体直接看Demo就好:

void Gameover() {fill(0);rect(width/2, height/2, 3*width/4, height*3/4);textAlign(CENTER);fill(255);textSize(45);text("You Die", width/2, height/2);textSize(20);text("left click to continue", width/2, height/2+100);text("right click to reset", width/2, height/2+150);
}

后记

其实个人对这个Demo不是很满意,太过简陋的碰撞检测,以及还有许多想法都没有实现,对Processing和Java本身也不是很擅长,代码基础不足。以后应该会继续完善这个想法,附录放上完整的代码、可执行文件和《代码本色》PDF版。

附录

《代码本色 The Nature of Code》:链接:https://pan.baidu.com/s/1nxk5u4oIBDiEuUsOFi9Syw 密码:h0sv
《Just Die》可执行文件:链接:https://pan.baidu.com/s/1WXzLKgeGWmTb8dZENuoSBA 密码:kutl
《Just Die》源文件:链接:https://pan.baidu.com/s/1AtQ9K9eBw393o-9kyJHgoA 密码:lp6v

这篇关于交互媒体技术应用——2D横版冒险游戏初尝试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/404870

相关文章

nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析(结合应用场景)

《nginx-t、nginx-sstop和nginx-sreload命令的详细解析(结合应用场景)》本文解析Nginx的-t、-sstop、-sreload命令,分别用于配置语法检... 以下是关于 nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析,结合实际应

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

Qt如何实现文本编辑器光标高亮技术

《Qt如何实现文本编辑器光标高亮技术》这篇文章主要为大家详细介绍了Qt如何实现文本编辑器光标高亮技术,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录实现代码函数作用概述代码详解 + 注释使用 QTextEdit 的高亮技术(重点)总结用到的关键技术点应用场景举例示例优化建议

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

Java中的登录技术保姆级详细教程

《Java中的登录技术保姆级详细教程》:本文主要介绍Java中登录技术保姆级详细教程的相关资料,在Java中我们可以使用各种技术和框架来实现这些功能,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录1.登录思路2.登录标记1.会话技术2.会话跟踪1.Cookie技术2.Session技术3.令牌技

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹