文章目录
- 1.租服务器配置环境 配置docker环境
- 创建工作用户ljh并赋予sudo权限
- 配置免密登录方式
- 给server1装环境装docker
- 将AC Terminal中的/var/lib/acwing/docker/images/docker_lesson_1_0.tar镜像上传到租好的服务器中
- 将镜像加载到本地
- 配置docker环境
- 创建项目
- 配置git
- 运行一下django
- Django创建app game
- 创建自己的页面
- 我如果把tmux强制关闭,如何再关闭我的server
- 2.创建菜单界面
- 项目的系统设计
- 文件的创建
- 全局设置
- 静态文件
- 创建HTML文件
- 写views文件
- 然后写路由
- 对于路由的理解
- 到此为止,文件结构就写好了,前期的准备工作完毕了
- 创建菜单的class
- 加菜单的选项
- 如何找bug
- 3.创建游戏界面
- 准备工作
- 创建运动物体的基类
- 创建地图
- 创建玩家
- 创建火球技能
- 碰撞检测
- 粒子效果
- 给所有的敌人随机一个颜色
- 再加一些效果
- 4.部署nginx与对接acapp
- 1. 增加容器的映射端口:80与443
- 2. 创建AcApp,获取域名、nginx配置文件及https证书
- 3.修改django项目的配置
- 配置uwsgi
- 5.填写信息
- 6. 使分配的域名生效
- 7.提交后点”打开应用”按钮,即可预览自己所写的应用
1.租服务器配置环境 配置docker环境
创建工作用户ljh并赋予sudo权限
登录到新服务器。打开AC Terminal,然后
ssh root@xxx.xxx.xxx.xxx # xxx.xxx.xxx.xxx替换成新服务器的公网IP
创建ljh用户:
adduser ljh # 创建用户ljh
usermod -aG sudo ljh # 给用户ljh分配sudo权限
配置免密登录方式
退回AC Terminal,然后配置ljh用户的别名和免密登录
配置别名,进入.ssh/里面
然后vim config
cd /home/acs/.ssh
vim config
vim /etc/ssh/sshd_config进去后如何编辑和保存
然后添加别名
Host server1
HostName ip地址
User ljh(用户名,一般不写root)
创建秘钥
ssh-keygen
免密登录,一键添加公钥
ssh-copy-id server1
然后就可以便捷的登录到自己的服务器上了
ssh server1
给server1装环境装docker
sudo apt-get update
sudo apt-get install tmux
然后logout
将AC Terminal的配置传到新服务器上:
scp .bashrc .vimrc .tmux.conf server1:
然后进入server1, 可以使用后tmux, 退出tmux, ctrl+d
安装tmux和docker
登录自己的服务器,然后安装tmux:(可省略)
sudo apt-get update
sudo apt-get install tmux
打开tmux。(养成好习惯,所有工作都在tmux里进行,防止意外关闭终端后,工作进度丢失)
然后在tmux中根据docker安装教程安装docker即可。
一般tmux, 快捷键的前缀是Ctrl b, 按完松开之后输 % 可以分栏,但是acwing这个改成了Ctrl a
Ctrl a + % 左右分屏
Ctrl a + “ 上下分屏
Ctrl a + x 结束这块分屏
Ctrl a + o 光标移动
tmux基本用法 tmux基本操作
然后通过tmux装docker,复制官网的命令就行
docker官网
通过docker --version判断是否装好
将AC Terminal中的/var/lib/acwing/docker/images/docker_lesson_1_0.tar镜像上传到租好的服务器中
cd /var/lib/acwing/docker/images/
scp docker_lesson_1_0.tar server1:
然后进入server1
将镜像加载到本地
sudo gpasswd -a ljh docker #将普通用户username加入到docker组
newgrp docker #更新docker组
然后就可以自由使用docker命令了
docker load -i django_lesson_1_0.tar # 将镜像加载到本地
docker images #查看就会多一个镜像
创建并运行django_lesson:1.0镜像
docker run -p 20000:22 -p 8000:8000 --name django_server -itd django_lesson:1.0
配置docker环境
进入docker 容器中
docker attach django_server
这也是一个虚拟服务器,我们还是创建一个非根用户
adduser ljh # 创建用户ljh
usermod -aG sudo ljh # 给用户ljh分配sudo权限
在容器里我设置的 ljh 的密码是 Ljh
退出容器,不能直接Ctrl+d, 我们要挂起容器
Ctrl+p 再 Ctrl+q
去云平台控制台中修改安全组配置,放行端口20000,ssh登录端口, 8000, django调试端口
管理控制台 - 实例 -更多 -网络与安全组 - 安全组配置 -配置规则, 然后手动添加
然后我们在我们租的服务器里,可以进入docker
ssh ljh@localhost -p 20000
可以进入到这个容器里 然后退出,这个退出 Ctrl + d 就可以了
然后我们退回到acterminal 进入.ssh配置一下别名
cd .ssh/
vim config
然后我配置了一个django, 之后只要ssh django就能进入容器了 密码是 Ljh
Host django
HostName 120.78.165.208
User ljh
Port 20000
然后可以配置一下免密登录, 直接 ssh-copy-id django
ssh-copy-id三步实现SSH无密码登录和ssh常用命令
然后就可以从ac terminal 去 ssh django
然后 退回到 acterminal , 把本地这些东西复制到容器里
scp .bashrc .vimrc .tmux.conf django:
这样 我们的docker 环境就配置好了
创建项目
我们进入django, 可以通过 django-admin --version去查看版本号
django-admin startproject acapp
然后我们可以用 ls 看一下多了一个acapp, tree .可以看一下目录结果
配置git
然后cd acapp,在这个目录下 git init , 就会多一个.git文件,这应该是本地git仓库
然后 cd 出来 然后 ssh-keygen,git是通过ssh实现的,所以需要ssh key
然后配置云端的,创建一个仓库来存储 在git.acwing.com创建一个新项目
按照提示,配置一下,Git global setup
git config --global user.name "LIU JIAHAO"
git config --global user.email "1059723379@qq.com"
再把本地的项目传上去,cd到acapp目录下,然后git add . git commit -m “start project”, 再写一个README.md , vim README.md, 然后把这个readme.md给 git add . git commit -m “add readme”
然后push一下现有的仓库
git remote add origin https://git.acwing.com/LJH2023/acapp.git
git push -u origin --all
git push -u origin --tags
这几行只用写一次,后面就直接git add git commit git push
执行的时候需要输密码, 我们可以cd出来 ,然后cd .ssh/ 然后cat id_rsa.pub, 把公钥复制下来, 然后点击git acwing头像 - 偏好设置 - ssh秘钥 ,把这个复制到上面。
因为git其实就是基于ssh的
然后就能发现项目被传到了git acwing
运行一下django
cd 进入 acapp目录
python3 manage.py runserver 0.0.0.0:8000
然后,在浏览器地址栏,输入自己的ip地址
120.78.165.208:8000
会报错,让You may need to add ‘120.78.165.208’ to ALLOWED_HOSTS.
如果不知道 ALLOWED_HOSTS在哪,可以
ag ALLOWED_HOSTS
他就会全文搜索这个字符串告诉你在哪, 然后cd 进去 , vim settings.py点开,把’120.78.165.208’加进去, 保存 就刷新了, 然后刷新一下网页页面
git status一下,会发现,出现了一个__pycache__/的东西,这是python预编译好的东西,我们git维护代码的时候不要维护它
我们cd 回acapp, 仓库的根目录,加一个.gitignore文件
vim .gitignore
然后写 **/pycache 保存
这样就会帮我们过滤掉
然后我们git add . git status, git commit -m " modify allowed", git push
由于我不会复制ac teminal的内容,我每次git push都得输入 账号密码
Django创建app game
python3 manage.py startapp game
然后就会多了一个game 文件夹
然后我们可以 ctrl z退出正在运行的后台
同步一下数据库的修改
python3 manage.py migrate
然后我们再运行python3 manage.py runserver 0.0.0.0:8000
在 120.78.165.208:8000/admin就能进入管理员界面了
我们需要创建一个管理员账号
python3 manage.py createsuperuser
我创建的用户名是 admin , 密码是Ljh
然后输入就可以登录了
创建自己的页面
cd game
touch urls.py
mkdir templates
ls
接下来整个项目操作的就是 models.py views.py urls.py templates
我们接下来 vim views.py
,写一个函数, 每一个页面就是对应这views里的一个函数
from django.http import HttpResponse
def index(request):
return HttpResponse(''我的第一个网页")
```
写完之后我们 vim urls.py
在这里插入代码,写一个路径,url就是一个路由,告诉要调用哪一个函数
from django.urls import path
from game.views import index
urlpatterns = [
path("", index, name = "index"),
]
我们game app的urls 还需要写到总url 里面, 我们cd 到 acapp/acapp, 打开url
from django.urls import path, include
然后在urlpattern里写一个path
path('game/', include('game.urls')),
这样的话我们登入 ip地址, /game 就能看见写的view了,如果我们写path(‘’, include(‘game.urls’)), 登入ip直接就是写的view
最后,http://120.78.165.208:8000 是这节课的作品
我买了一个轻量级应用 地址 8.134.129.207:8000
我如果把tmux强制关闭,如何再关闭我的server
首先查看已存在端口号列表:
然后把相应进程的pid 给kill了
ps aux | grep -i manage
kill -9 进程的pid
最后我在本地的gitbash创建了别名和免密登录,只要ssh server1 / ssh django
2.创建菜单界面
8.134.129.207:8000
cd acapp然后
python3 manage.py runserver 0.0.0.0:8000
项目的系统设计
项目系统设计
menu:菜单页面
playground:游戏界面
settings:设置界面
项目文件结构
templates目录:管理html文件
urls目录:管理路由,即链接与函数的对应关系
views目录:管理http函数
models目录:管理数据库数据
static目录:管理静态文件,比如:
-css:对象的格式,比如位置、长宽、颜色、背景、字体大小等
-js:对象的逻辑,比如对象的创建与销毁、事件函数、移动、变色等
-image:图片
-audio:声音
…
consumers目录:管理websocket函数
文件的创建
rm urls.py views.py models.py
mkdir urls 和 views 和 models 和 static
要想在不同python文件夹之间import需要在文件夹下面创建__init__.py
分别进入 urls views models 目录下, touch __init__.py
全局设置
时区
在acapp/acapp下 vim settings.py
在108行把时区改成 Asia/Shanghai
在acapp/game中有一个 apps.py 抄到acapp/acapp/settings 中的33行
'game.apps.GameConfig',
静态文件
设置一下静态文件的地址,在settings.py最后121行,先在13行左右import os
再在121行加上 STATIC_ROOT = os.path.join(BASE_DIR, ‘static’)
在125行加上MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
static文件就是让本地的文件通过链接来返回的
然后我们进入cd acapp/game/static/image
创建 mkdir menu playground settings
然后我们cd menu
wget --output-document=background.gif 动图的链接
就在静态文件下面的image下面的menu里下载好了
然后在8.134.129.207:8000/static/image/menu/background.gif应该能看到,但是我看不到
以后想放声音的话也可以放在static目录下
然后我们
cd acapp/game/static/css
vim game.css
一个项目的css文件也就这一个了,css文件不用做拆分
然后我们
cd acapp/game/static/js
mkdir dist src
#dist一般存我们最终使用的js文件,src一般存源文件
这个src有很多文件,我们可以写一个脚本把这些合并然后放到src里面
我们cd acapp
mkdir scripts ##### 创建一个文件夹,把项目里用到的脚本放在这里
vim compress_game_js.sh
#! /bin/bash
JS_PATH=/home/ljh/acapp/game/static/js/
JS_PATH_DIST=${JS_PATH}dist/
JS_PATH_SRC=${JS_PATH}src/
find $JS_PATH_SRC -type f -name '*.js' | sort | xargs cat > ${JS_PATH_DIST}game.js
给权限~/acapp/scripts$ chmod +x compress_game_js.sh
执行一下~/acapp/scripts$ ./compress_game_js.sh
会发现在:~/acapp/game/static/js/dist 多一个 game.js
创建HTML文件
创建HTML文件还是要创建三个不同的文件夹,因为对应的html文件会很多
cd acapp/game/templates
mkdir menu playground settings
因为以后可以多终端的跑这个游戏
mkdir multiends
cd multiends
vim web.html #web端
前端的工具很多,主流的是vue, 但是这里用了简单的jquery
前端界面是客户端js渲染的,前后端分离,减轻服务器的压力
{% load static %}
<head>
<link rel="stylesheet" href="https://cdn.acwing.com/static/jquery-ui-dist/jquery-ui.min.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
<script src="{% static 'js/dist/game.js' %}"></script>
</head>
<body style="margin: 0">
<div id="ac_game_12345678"></div>
<script>
$(document).ready(function(){
let ac_game = new AcGame("ac_game_12345678"); #一定要new
});
</script>
</body>
cd acapp/game/static/js/src
mkdir menu playground settings
vim zbase.js #排序是字典序,z排在最后
这个是js的构造函数,html文件被返回给前端,前端发现有一个js需要执行,创建一个acgame的class,调用这个构造函数
class AcGame {
constructor(id) {
}
}
写完之后在cd acapp/scrpts 执行./compress_game_js.sh
写views文件
cd acapp/game/views
mkdir menu playground settings
这是一个python 文件夹
所以 在三个文件夹分别 cd…/menu/ 然后touch init.py
然后出来,创建一个总的函数 vim index.py 主要是用来返回我们刚刚用写的html文件的
from django.shortcuts import render
def index(request):
return render(request, "multiends/web.html")
然后写路由
cd acapp/game/urls
mkdir menu playground settings
这是一个python 文件夹
所以 在三个文件夹分别 cd…/menu/ 然后touch init.py
创建总的函数 vim index.py ,把文件夹下的三个文件夹里面的路径include起来的作用
from django.urls import path, include
urlpatterns = [
]
先去这三个子文件夹里
vim index.py
from django.urls import path
urlpatterns = [
]
然后回到总文件 cd …
1 from django.urls import path, include
2 from game.views.index import index
3
4
5 urlpatterns = [
6 path("", index, name="index"),
7 path("menu/", include("game.urls.menu.index")),
8 path("playground/", include("game.urls.playground.index")),
9 path("settings/", include("game.urls.settings.index")),
10 ]
然后cd acapp/acapp 修改一下全局的路由
vim urls.py
path('', include('game.urls.index')),
对于路由的理解
解释一下,整个Django项目, 访问一个链接,它会优先进到全局的urls里面。进来之后他会去匹配,每一项是用/隔开的,第一个字符串如果是…,就会进入它的路由;比如
path(‘’, include(‘game.urls.index’)), 如果字符串是空的,它就会进入game.urls.index
那么game.urls.index是这样的
from django.urls import path, include
from game.views.index import index
urlpatterns = [
path("", index, name="index"),
path("menu/", include("game.urls.menu.index")),
path("playground/", include("game.urls.playground.index")),
path("settings/", include("game.urls.settings.index")),
]
如果下一个还是空的,它会进入index函数,这个函数是game.views.index
那么vies.index是这样的
from django.shortcuts import render
def index(request):
return render(request, "multiends/web.html")
那么这个index函数会去渲染一个html页面,也就是会返回一个字符串,这个内容就是multiends/web.html里面的内容,这个内容就是templates里面的multiends里面的.html
就会返回
<head>
<link rel="stylesheet" href="https://cdn.acwing.com/static/jquery-ui-dist/jquery-ui.min.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
<script src="{% static 'js/dist/game.js' %}"></script>
</head>
<body style="margin: 0">
<div id="ac_game_12345678"></div>
<script>
$(document).ready(function(){
let ac_game = AcGame("ac_game_12345678");
});
</script>
</body>
到此为止,文件结构就写好了,前期的准备工作完毕了
创建菜单的class
cd acapp/game/static/js/src/menu/
记得js文件改动之后都要执行一下更新合并脚本 cd acapp/scripts 执行 ./compress_game_js.sh
vim zbase.js
1 class AcGameMenu {
2 constructor(root) {
3 this.root = root
4 this.$menu = $(`
5 <div class="ac-game-menu">
6 </div>
7 `);
8 this.root.$ac_game.append(this.$menu);
9 }
10 }
设置背景的样式cd acapp/game/static/css, vim game.css
.ac-game-menu {
width: 100%;
height: 100%;
background-image: url("/static/image/menu/background.gif");
background-size: 100% 100%;
}
设置完之后把这个加到总对象,cd acapp/game/static/js/src/, vim zbase.js
class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id);
this.menu = new AcGameMenu(this);
}
}
加菜单的选项
cd acapp/game/static/js/src/menu/
class AcGameMenu {
constructor(root) {
this.root = root;
this.$menu = $(`
<div class="ac-game-menu">
<div class="ac-game-menu-field">
<div class="ac-game-menu-field-item ac-game-menu-field-item-single-mode">
单人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-multi-mode">
多人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-settings">
退出
</div>
</div>
</div>
`);
this.$menu.hide();
this.root.$ac_game.append(this.$menu);
this.$single_mode = this.$menu.find('.ac-game-menu-field-item-single-mode');
this.$multi_mode = this.$menu.find('.ac-game-menu-field-item-multi-mode');
this.$settings = this.$menu.find('.ac-game-menu-field-item-settings');
this.start();
}
start() {
this.add_listening_events();
}
add_listening_events() {
let outer = this;
this.$single_mode.click(function(){
outer.hide();
outer.root.playground.show("single mode");
});
this.$multi_mode.click(function(){
outer.hide();
outer.root.playground.show("multi mode");
});
this.$settings.click(function(){
outer.root.settings.logout_on_remote();
});
}
show() { // 显示menu界面
this.$menu.show();
}
hide() { // 关闭menu界面
this.$menu.hide();
}
}
设置一下框的样式 cd acapp/game/static/css, vim game.css
.ac-game-menu {
width: 100%;
height: 100%;
background-image: url("/static/image/menu/background.gif");
background-size: 100% 100%;
}
.ac-game-menu-field {
width: 20vw;
position: relative;
top: 40vh;
left: 19vw;
}
.ac-game-menu-field-item {
color: white;
height: 7vh;
width: 18vw;
font-size: 6vh;
font-style: italic;
padding: 2vh;
text-align: center;
background-color: rgba(39,21,28, 0.6);
border-radius: 10px;
letter-spacing: 0.5vw;
cursor: pointer;
}
.ac-game-menu-field-item:hover {
transform: scale(1.2);
transition: 100ms;
}
如何找bug
首先网页地址是 8.134.129.207:8000
cd acapp 运行python3 manage.py runserver 0.0.0.0:8000
按F12 在console里面可以找到报错,可以对着https://git.acwing.com/yxc/acapp 改
3.创建游戏界面
cd acapp/game/templates/multiends vim web.html
改成模块化的引用,作为上节课一个内容的完善
{% load static %}
<head>
<link rel="stylesheet" href="https://cdn.acwing.com/static/jquery-ui-dist/jquery-ui.min.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
</head>
<body style="margin: 0">
<div id="ac_game_12345678"></div>
<script type="module">
import {AcGame} from "{% static 'js/dist/game.js' %}";
$(document).ready(function(){
let ac_game = new AcGame("ac_game_12345678");
});
</script>
</body>
cd acapp/game/static/js/src
export class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id);
this.menu = new AcGameMenu(this);
this.playground = new AcGamePlayground(this);
this.start();
}
start() {
}
}
记得cd acapp/scripts ./compress_game_js.sh
准备工作
我们这节课要做playground所以方便调试,把js/src里面的zbase.js里面的 this.menu注释掉了
然后cd acapp/game/static/js/src/playground, vim zbase.js
注释掉this.hide,
1 class AcGamePlayground {
2 constructor(root) {
3 this.root = root;
4 this.$playground = $(`<div class="ac-game-playground"></div>`);
5
6 // this.hide();
7 this.root.$ac_game.append(this.$playground);
8
9 this.start();
打开css文件 cd acapp/game/static/css, vim game.css
37 .ac-game-playground {
38 width: 100%;
39 height: 100%;
40 user-select: none;
cd acapp/game/static/js/src/playground, vim zbase.js
记录一下画布的高宽
1 class AcGamePlayground {
2 constructor(root) {
3 this.root = root;
4 this.$playground = $(`<div class="ac-game-playground"></div>`);
5
6 // this.hide();
7 this.root.$ac_game.append(this.$playground);
8 this.width = this.$playground.width();
9 this.height = this.$playground.height();
......
......
创建运动物体的基类
cd acapp/game/static/js/src/playground, mkdir ac_game_object创建一个物体运动的基类
cd ac_game_object, vim zbase.js 创建一个简易的虚拟引擎
1 let AC_GAME_OBJECTS = [];
2
3 class AcGameObject {
4 constructor() {
5 AC_GAME_OBJECTS.push(this);
6
7 this.has_called_start = false; //是否执行过start函数
8 this.timedelta = 0; //当前帧距离上一帧的时间间隔
9 }
10
11 start() { //只会在第一帧执行一次
12 }
13
14 update() { //每一帧都会执行一次
15 }
16 ----
17 on_destroy() { //被销毁前执行一次
18 }
19
20 destroy() { //删掉该物体
21 this.on_destroy();
22
23 for (let i = 0; i < AC_GAME_OBJECTS.length; i ++) {
24 if (AC_GAME_OBJECTS[i] === this) {
25 AC_GAME_OBJECTS.splice(i, 1);
26 break;
27 }
28 }
29 }
30 }
31
32 let last_timestamp;
33 let AC_GAME_ANIMATION = function(timestamp) {
34 for (let i = 0; i < AC_GAME_OBJECTS.length; i ++) {
35 let obj = AC_GAME_OBJECTS[i];
36 if (!obj.has_called_start) {
37 obj.start();
38 obj.has_called_start = true;
39
40 } else {
41 obj.timedelta = timestamp - last_timestamp;
42 obj.update();
43 }
44 }
45 last_timestamp = timestamp;
46
47 requestAnimationFrame(AC_GAME_ANIMATION);
48
49 }
50
51
52 requestAnimationFrame(AC_GAME_ANIMATION); //一秒钟会调用60次
创建地图
cd acapp/game/static/js/src/playground/game_map, mkdir game_map, cd game_map创建一个游戏地图的对象
vim zbase.js
1 class GameMap extends AcGameObject {
2 constructor(playground) {
3 super();
4 this.playground = playground;
5 this.$canvas = $(`<canvas></canvas>`);
6 this.ctx = this.$canvas[0].getContext('2d');
7 this.ctx.canvas.width = this.playground.width;
8 this.ctx.canvas.height = this.playground.height;
9 this.playground.$playground.append(this.$canvas);
10
11
12 }
13
14 start() {
15 }
16
17 update() { this.render();
18 }
19
20
21 render() {
22 this.ctx.fillStyle = "rgba(0, 0, 0)";
23 this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
24 }
25 }
把地图对象放进playground里,cd acapp/game/static/js/src/playground, vim zbase.js
1 class AcGamePlayground {
2 constructor(root) {
3 this.root = root;
4 this.$playground = $(`<div class="ac-game-playground"></div>`);
5
6 // this.hide();
7 this.root.$ac_game.append(this.$playground);
8 this.width = this.$playground.width();
9 this.height = this.$playground.height();
10 this.game_map = new GameMap(this); //添加进playground按F12才能看见canvas
11 this.start();
12 }
13
14 start() {
15 }
16
17 show() { //打开playground界面
18 this.$playground.show();
19 }
20
21 hide() { //关闭playground界面
22 this.$playground.hide();
23 }
24}
创建玩家
cd acapp/game/static/js/src/playground, mkdir player
cd player
vim zbase.js
1 class Player extends AcGameObject {
2 constructor (playground, x, y, radius, color, speed, is_me) {
3 super();
4 this.playground = playground;
5 this.ctx = this.playground.game_map.ctx;
6 this.x = x;
7 this.y = y;
8 this.vx = 0;
9 this.vy = 0;
10 this.move_length = 0;
11 this.radius = radius;
12 this.color = color;
13 this.speed = speed;
14 this.is_me = is_me;
15 this.eps = 0.1;
16
17 }
18
19 start() {
20 if (this.is_me) {
21 this.add_listening_events();
22 }
23 }
24
25 add_listening_events() {
26 let outer = this;
27 this.playground.game_map.$canvas.on("contextmenu", function(){ //取消右键的菜单
28 return false;
29 });
30 this.playground.game_map.$canvas.mousedown(function(e) { //获取右键点击的坐标
31 if (e.which === 3) {
32 outer.move_to(e.clientX, e.clientY);
33
34 }
35
36 });
37 }
38
39 get_dist(x1, y1, x2, y2) {
40 let dx = x1 - x2;
41 let dy = y1 - y2;
42 return Math.sqrt(dx * dx + dy * dy);
43
44 }
45
46 move_to(tx, ty) {
47 this.move_length = this.get_dist(this.x, this.y, tx, ty); //移动的模长
48 let angle = Math.atan2(ty - this.y, tx - this.x); //求移动向量的角度
49 this.vx = Math.cos(angle); //表示速度,其实是1*cos(angle)
50 this.vy = Math.sin(angle);
51
52 }
53
54 update() {
55 if (this.move_length < this.eps) {
56 this.move_length = 0;
57 this.vx = this.vy = 0;
58 } else {
59 let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000); //不能移出界,moved表示的是每秒真实移动的距离
60 this.x += this.vx * moved;
61 this.y += this.vy * moved;
62 this.move_length -= moved;
63 }
64 this.render();
65 }
66
67 render() {
68 this.ctx.beginPath();
69 this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
70 this.ctx.fillStyle = this.color;
71 this.ctx.fill();
72 }
73 }
当然,玩家这个对象也是会放进playground里,cd acapp/game/static/js/src/playground, vim zbase.js
1 class AcGamePlayground {
2 constructor(root) {
3 this.root = root;
4 this.$playground = $(`<div class="ac-game-playground"></div>`);
5
6 // this.hide();
7 this.root.$ac_game.append(this.$playground);
8 this.width = this.$playground.width();
9 this.height = this.$playground.height();
10 this.game_map = new GameMap(this);
11 this.players = [];
12 this.players.push(new Player(this, this.width / 4, this.height / 4, this.height * 0.1, "white", this.height * 0.15, true));
13 this.start();
14 }
15
16 start() {
17 }
18
19 show() { //打开playground界面
20 this.$playground.show();
21 }
22
23 hide() { //关闭playground界面
24 this.$playground.hide();
25 }
26
27
28 }
创建火球技能
cd acapp/game/static/js/src/playground,
mkdir skill, cd skill
mkdir fireball, cd fireball, vim zbase.js
创建火球类,其实也就是像player一样一个移动的球
然后在player类里实现释放技能的操作,cd acapp/game/static/js/src/playground/player,
然后可以在playground里多加几个敌人
cd acapp/game/static/js/src/playground
然后可以在player里给敌人添加简单的ai,
cd acapp/game/static/js/src/playground/player,
碰撞检测
在fireball里面,cd acapp/game/static/js/src/playground/skill/fireball, vim zbase,js
实现一个is_collision()函数,检测是否发生碰撞,和一个attack()函数,碰撞之后
class FireBall extends AcGameObject {
constructor(playground, player, x, y, radius, vx, vy, color, speed, move_length, damage) {
super();
this.playground = playground;
this.player = player;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.radius = radius;
this.color = color;
this.speed = speed;
this.move_length = move_length;
this.damage = damage;
this.eps = 0.1;
}
start() {
}
update() {
if (this.move_length < this.eps) {
this.destroy();
return false;
}
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.move_length -= moved;
for (let i = 0; i < this.playground.players.length; i ++) {
let player = this.playground.players[i];
if (this.player !== player && this.is_collision(player)) {
this.attack(player);
}
}
this.render();
}
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2, dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
is_collision(player) {//圆心距离小于等于半径之和就是碰撞
let distance = this.get_dist(this.x, this.y, player.x, player.y);
if (distance < this.radius + player.radius)
return true;
return false;
}
attack(player) {
let angle = Math.atan2(player.y - this.y, player.x - this.x);
player.is_attacked(angle, this.damage);
this.destroy();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
cd acapp/game/static/js/src/playground/player, 在player里面写一个被击中的函数, vim zbase.js
粒子效果
cd acapp/game/static/js/src/playground,
mkdir particle, cd particle , vim zbase.js
class Particle extends AcGameObject {
constructor(playground, x, y, radius, vx, vy, color, speed, move_length) {
super();
this.playground = playground;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.radius = radius;
this.vx = vx;
this.vy = vy;
this.color = color;
this.speed = speed;
this.move_length = move_length;
this.friction = 0.9;
this.eps = 1;
}
start() {
}
update() {
if (this.move_length < this.eps || this.speed < this.eps) {//move_length或者速度为0就消失
this.destroy();
return false;
}
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
this.x += this.vx * moved;
this.y += this.vy * moved;
this.speed *= this.friction;
this.move_length -= moved;
this.render();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
然后 cd acapp/game/static/js/src/playground/player, 在player里面被击中的函数里,改动效果, vim zbase.js
给所有的敌人随机一个颜色
cd acapp/game/static/js/src/playground,vim zbase.js
在playground里面写一个get_color函数就行
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`<div class="ac-game-playground"></div>`);
// this.hide();
this.root.$ac_game.append(this.$playground);
this.width = this.$playground.width();
this.height = this.$playground.height();
this.game_map = new GameMap(this);
this.players = [];
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, "white", this.height * 0.15, true));
for (let i = 0; i < 5; i ++) {
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, this.get_random_color(), this.height * 0.15, false));
}
this.start();
}
get_random_color() {
let colors = ["blue", "orange", "yellow", "pink", "green"];
return colors[Math.floor(Math.random() * 5)];
}
start() {
}
show() { //打开playground界面
this.$playground.show();
}
hide() { //关闭playground界面
this.$playground.hide();
}
}
再加一些效果
敌人可以发火球去攻击我们,在player下的.js写
class Player extends AcGameObject {
constructor (playground, x, y, radius, color, speed, is_me) {
super();
this.playground = playground;
this.ctx = this.playground.game_map.ctx;
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.damage_x = 0;
this.damage_y = 0;
this.damage_speed = 0;
this.move_length = 0;
this.radius = radius;
this.color = color;
this.speed = speed;
this.is_me = is_me;
this.eps = 0.1;
this.friction = 0.9;
this.spent_time = 0;
this.cur_skill = null;
}
start() {
if (this.is_me) {//如果是自己的话,调用监听事件
this.add_listening_events();
} else {//如果不是自己
let tx = Math.random() * this.playground.width;
let ty = Math.random() * this.playground.height;
this.move_to(tx, ty);
}
}
add_listening_events() {//监听事件,鼠标点击,按键盘
let outer = this;
this.playground.game_map.$canvas.on("contextmenu", function(){ //取消右键的菜单
return false;
});
this.playground.game_map.$canvas.mousedown(function(e) { //获取右键点击的坐标
if (e.which === 3) {
outer.move_to(e.clientX, e.clientY);
} else if(e.which === 1) {//点的是鼠标左键的话
if (outer.cur_skill === "fireball") {//如果当前技能是火球的话
outer.shoot_fireball(e.clientX, e.clientY);//朝tx,ty坐标发火球
}
outer.cur_skill = null;//左键点完发完火球之后,这个状态清空
}
});
$(window).keydown(function(e) {//获取键盘信息
if (e.which === 81) {//百度keycode,js键盘按钮81代表q键
outer.cur_skill = "fireball";
return false;//代表后续不处理了
}
});
}
shoot_fireball(tx, ty) {
let x = this.x;
let y = this.y;
let radius = this.playground.height * 0.01;
let angle = Math.atan2(ty - this.y, tx - this.x);
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = "orange";
let speed = this.playground.height * 0.5;
let move_length = this.playground.height * 1;
new FireBall(this.playground, this, x, y, radius, vx, vy, color, speed, move_length, this.playground.height * 0.01);
}
get_dist(x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
}
move_to(tx, ty) {
this.move_length = this.get_dist(this.x, this.y, tx, ty); //移动的模长
let angle = Math.atan2(ty - this.y, tx - this.x); //求移动向量的角度
this.vx = Math.cos(angle); //表示速度,其实是1*cos(angle)
this.vy = Math.sin(angle);
}
is_attacked(angle, damage) {
for (let i = 0; i < 10 + Math.random() * 5; i ++) {//被击打之后的粒子效果,随机出现一些粒子
let x = this.x, y = this.y;
let radius = this.radius * Math.random() * 0.1;
let angle = Math.PI * 2 * Math.random();
let vx = Math.cos(angle), vy = Math.sin(angle);
let color = this.color;
let speed = this.speed * 10;
let move_length = this.radius * Math.random() * 5;
new Particle(this.playground, x, y, radius, vx, vy, color, speed, move_length);
}
this.radius -= damage;
if (this.radius < 10) {
this.destroy();
return false;
}
this.damage_x = Math.cos(angle);
this.damage_y = Math.sin(angle);
this.damage_speed = damage * 100;
}
update() {
this.spent_time += this.timedelta / 1000;
if (! this.is_me && this.spent_time > 5 && Math.random() < 1 / 300.0) {
let player = this.playground.players[Math.floor(Math.random() * this.playground.players.length)];
let tx = player.x + player.speed * this.vx * this.timedelta / 1000 * 0.5;
let ty = player.y + player.speed * this.vy * this.timedelta / 1000 * 0.5;
this.shoot_fireball(tx, ty);
}
if (this.damage_speed > 10) {
this.vx = this.vy = 0;
this.move_length = 0;
this.x += this.damage_x * this.damage_speed * this.timedelta / 1000;
this.y += this.damage_y * this.damage_speed * this.timedelta / 1000;
this.damage_speed *= this.friction;
} else {
if (this.move_length < this.eps) {
this.move_length = 0;
this.vx = this.vy = 0;
if (!this.is_me) {//对于robots,不能停,循环着随机移动
let tx = Math.random() * this.playground.width;
let ty = Math.random() * this.playground.height;
this.move_to(tx, ty);
}
} else {
let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000); //不能移出界,moved表示的是每秒真实移动的距离
this.x += this.vx * moved;
this.y += this.vy * moved;
this.move_length -= moved;
}
}
this.render();
}
render() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
on_destroy() {
for (let i = 0; i < this.playground.players.length; i ++) {
if (this.playground.players[i] === this) {
this.playground.players.splice(i, 1);
}
}
}
}
以上就是这节增加的代码,主要是创建一个player类和fireball类, particle类
4.部署nginx与对接acapp
1. 增加容器的映射端口:80与443
第一步,登录容器,关闭所有运行中的任务。
第二步,登录运行容器的服务器,然后执行:
docker ps #可以查看容器
docker commit CONTAINER_NAME django_lesson:1.1 # 将容器保存成镜像,将CONTAINER_NAME替换成容器名称
docker images #可以查看镜像
docker stop CONTAINER_NAME # 关闭容器
docker rm CONTAINER_NAME # 删除容器
# 使用保存的镜像重新创建容器
# 增加了两个端口,80是http的默认,443是https的默认端口, 20000是负责登录的, 8000是负责调试的
docker run -p 20000:22 -p 8000:8000 -p 80:80 -p 443:443 --name django_server -itd django_lesson:1.1
第三步,去云服务器控制台,在安全组配置中开放80和443端口。
我买的阿里云轻量级服务器本身就开放了;这个限制ip来源0.0.0.0/0意思是允许所有ip来访问
我们用创建的镜像,重新生成容器的时候,公钥以及相关配置文件都不会变,所以我们还是可以直接ssh django进我们的新容器
2. 创建AcApp,获取域名、nginx配置文件及https证书
打开AcWing应用中心,点击“创建应用”的按钮。
系统分配的域名、nginx配置文件及https证书在如下位置:
在服务器IP一栏填入自己服务器的ip地址。都是sudo vim, :set paste然后粘上去
将nginx.conf中的内容写入服务器/etc/nginx/nginx.conf文件中。如果django项目路径与配置文件中不同,注意修改路径。
将acapp.key中的内容写入服务器/etc/nginx/cert/acapp.key文件中。
将acapp.pem中的内容写入服务器/etc/nginx/cert/acapp.pem文件中。
然后启动nginx服务:
sudo /etc/init.d/nginx start
3.修改django项目的配置
cd acapp/acapp
打开settings.py文件:
大约在二十七八行左右
将分配的域名添加到ALLOWED_HOSTS列表中。注意只需要添加https://后面的部分。
令DEBUG = False。
归档static文件:
cd acapp,
python3 manage.py collectstatic
然后在acapp就有static文件了
配置uwsgi
桥梁,效率更高,支持多并发
在django项目中添加uwsgi的配置文件:scripts/uwsgi.ini,内容如下:
cd acapp/scripts, vim uwsgi.ini
[uwsgi]
socket = 127.0.0.1:8000
chdir = /home/acs/acapp
wsgi-file = acapp/wsgi.py
master = true
processes = 2
threads = 5
vacuum = true
启动uwsgi服务:
cd acapp
uwsgi --ini scripts/uwsgi.ini
5.填写信息
标题:应用的名称
关键字:应用的标签(选填)
css地址:css文件的地址,例如:https://app4189.acapp.acwing.com.cn/static/css/game.css
js地址:js文件的地址:例如:https://app4189.acapp.acwing.com.cn/static/js/dist/game.js
主类名:应用的main class,例如AcGame。
图标:4:3的图片
应用介绍:介绍应用,支持markdown + latex语法。
改动了一些代码的逻辑,比如幕布之类的,以及把js文件的打包脚本加了传给acapp static的语句
下次执行打包要 cd acapp, ./scripts/compress_game_js.sh
6. 使分配的域名生效
填写完服务器IP之后,点“保存”或者“提交”按钮,均可使分配的域名生效。
7.提交后点”打开应用”按钮,即可预览自己所写的应用
等项目调试完之后,可以申请发布应用。审核通过后,你的acapp应用就可以与大家见面啦!