交互媒体技术应用——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

相关文章

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit