Курсовая_Андрей
.docxМИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В.И. Ульянова (Ленина)
Кафедра БТС
отчет
по курсовой работе
по дисциплине «Технологии разработки программных комплексов медицинских информационных систем»
Тема: Виртуальный симулятор для обучения СЛР
Студент гр. 7503 |
|
Дегилевич А.А. |
Преподаватель |
|
Калиниченко А.Н. |
Санкт-Петербург
2022
КУРСОВАЯ РАБОТА
«ВИРТУАЛЬНЫЙ СИМУЛЯТОР ДЛЯ ОБУЧЕНИЯ СЛР»
Описание работы:
Основное меню представлено на рисунке 1. Оно представляет собой окно, на которое попадает пользователь при включении симулятора. Через главное меню можно перейти в следующие разделы симулятора:
1) Меню выбора режима, представленное на рисунке 2. При наведении на кнопку режима изменяется изображение превью режима и его описание на соответствующее. Существует три основных режима:
- Симуляция – основной режим симулятора, который будет разработан в дальнейшем, в котором пользователь будет проводить СЛР виртуальному пострадавшему.
- Тренировка компрессий – режим, чтобы отработать практический навык компрессий, в отрыве от остального алгоритма (подробнее дальше).
Рисунок 1 – Главное меню
Рисунок 2 – Меню выбор режимов
Теоретический тест – режим для проверки теоретических знаний пользователя (рисунок 3).
Рисунок 3 – Режим теста
2) Теоретический раздел (рисунок 4). В этом разделе представлены основные теоретические знания о СЛР, а именно алгоритм, основные нюансы и ошибки проведения.
Рисунок 4 – Теоретический раздел
3) Меню настроек. В этом разделе содержаться настройки симулятора. На данный момент единственной регулируемой настройкой является язык симулятора.
Также разработан экран загрузки, представленный на рисунке 5. Он необходим для подгруздки необходимый объектов и данных при переходе между режимами. Во время загрузки пользователю демонстрируется советы и интересные факты о СЛР, которые помогут ему усвоить теоретические знания.
Рисунок 5 – Экран загрузки
Режим тренировки компрессиям работает следующим образом (рисунок 6). Справа налево по экрану двигаются полоски с частотой следования 110 раз/мин. Посередине экрана расположена область при нахождении полоски, в которой, пользователь может нажать на кнопку space, чтобы засчиталась компрессия. В зависимости от того, насколько точно пользователь попал в тайминг отображается различный визуальный эффект, и полоска окрашивается в соответственный цвет. Если полоска прошла зону, а компрессия не произошла то она становится красной.
Рисунок 6 – Режим оценки компрессий
В нижней части экрана расположены две динамические шкалы с цифровой расшифровкой. На этих маркерах после каждой компрессии отображается значение текущей частоты и глубины компрессий соответственно. Вместе с изменением числа перемещается указатель на шкале, который визуально объясняет пользователю в какую сторону ему следует изменить скорость или глубину компрессий.
Для дополнительной мотивации при обучении режим выполнен в игровом формате. За каждую успешно выполненную компрессию оператор получает очки, которые отображаются в верхнем левом углу.
После того как будут совершены 30 компрессий открывается окно результатов, в котором показывается итоговый счет, среднее значение частоты и глубины компрессий.
КОД ПРОГРАММЫ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Linq;
using System;
using UnityEngine.Localization.Settings;
public class BeatScroller : MonoBehaviour
{
private GameObject BeatScrollerGO;
private GameObject Beat;
public float Tempo;
private float Speed;
public bool Started = false;
private float time;
private float timeLeft = 0f;
public List<float> ButtonPressTime;
public List<float> TimeBetween;
public List<int> ButtonPressClass;
public List<float> Depth;
public GameObject BeatSprite;
public TMP_Text ScoreText;
private string ScoreLocalize;
public static BeatScroller instance;
private int Score = 0;
private int ScorePerBeat = 50;
private int NumBeats = 0;
private int NumBeatsPassed = 0;
private int NumBeatsPressed = 0;
public GameObject ResultsUI, ScoreUI;
public TMP_Text FreqResultText;
public TMP_Text DepthResultText;
public double MeanFreq;
public double MeanDepth;
public TMP_Text BPMLevelText;
public TMP_Text DepthLevelText;
public GameObject BPMPointer;
public GameObject DepthPointer;
private double IdealDepth = 5;
private double BPMHigh;
private double BPMLow;
private double DepthHigh;
private double DepthLow;
void Start()
{
instance = this;
BeatScrollerGO = gameObject;
Speed = Tempo * 8 / 60f;
time = 1 / (Tempo / 60f);
ScoreLocalize = LocalizationSettings.StringDatabase.GetLocalizedString("BPMTrain", "Score");
ScoreText.text = ScoreLocalize + Score;
BPMHigh = Tempo + 30;
BPMLow = Tempo - 30;
DepthHigh = IdealDepth + 3;
DepthLow = IdealDepth - 3;
}
void Update()
{
if (Started)
{
if (timeLeft > 0)
{
timeLeft -= Time.deltaTime;
}
else
{
if (NumBeats < 30)
{
SpawnBeat();
NumBeats++;
}
else if (NumBeatsPassed < 30)
{
timeLeft = time;
NumBeats++;
}
else
{
ShowResults();
}
}
transform.position -= new Vector3(Speed * Time.deltaTime, 0f, 0f);
}
}
void SpawnBeat()
{
Beat = Instantiate(BeatSprite, new Vector3(22, 0, 0), transform.rotation);
Beat.transform.SetParent(BeatScrollerGO.transform);
timeLeft = time;
}
public void BeatHit(int Multiplier)
{
Score += ScorePerBeat * Multiplier;
ScoreText.text = ScoreLocalize + Score;
ButtonPressTime.Add(NumBeats * time - timeLeft);
ButtonPressClass.Add(Multiplier);
if (NumBeatsPressed > 0)
{
TimeBetween.Add(ButtonPressTime[NumBeatsPressed] - ButtonPressTime[NumBeatsPressed - 1]);
double CurrentBPM = Math.Round(60 / TimeBetween[NumBeatsPressed - 1], 0);
BPMLevelText.text = CurrentBPM.ToString();
double PercentBPM = (CurrentBPM - BPMLow)/(BPMHigh - BPMLow);
BPMPointer.GetComponent<Pointer>().ChangeDestination(PercentBPM);
}
else
BPMLevelText.text = "110";
NumBeatsPressed++;
NumBeatsPassed++;
}
public void DepthScore(float CurrentDepth)
{
DepthLevelText.text = Math.Round(CurrentDepth, 2).ToString();
Depth.Add(CurrentDepth);
double PercentDepth = (CurrentDepth - DepthLow) / (DepthHigh - DepthLow);
DepthPointer.GetComponent<Pointer>().ChangeDestination(PercentDepth);
}
public void BeatMiss()
{
NumBeatsPassed++;
}
void ShowResults()
{
MeanFreq = Math.Round(60 / TimeBetween.Average(), 2);
MeanDepth = Math.Round(Depth.Average(), 2);
BPMModeManager.instance.ShowResults();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BeatObject : MonoBehaviour
{
public bool CanBePressed;
public KeyCode KeyToPress;
private SpriteRenderer SR;
private bool Hit;
private float Depth = 0;
private AudioSource AudioSource;
public AudioClip BeatSound;
public GameObject GreatEffect, PerfectEffect, MissEffect;
// Start is called before the first frame update
void Start()
{
SR = GetComponent<SpriteRenderer>();
Destroy(gameObject, 3f);
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyToPress))
{
if(CanBePressed)
{
Hit = true;
if (Mathf.Abs(transform.position.x) > 0.5)
{
BeatScroller.instance.BeatHit(1);
SR.color = new Color(0.4f, 1f, 0.4f);
Instantiate(GreatEffect, new Vector3(0, 0, 0), transform.rotation);
GetComponent<BoxCollider2D>().enabled = false;
}
else
{
BeatScroller.instance.BeatHit(2);
SR.color = new Color(1f, 1f, 0.4f);
Instantiate(PerfectEffect, new Vector3(0, 0, 0), transform.rotation);
GetComponent<BoxCollider2D>().enabled = false;
}
BeatScroller.instance.DepthScore(Depth);
//AudioSource.PlayOneShot(BeatSound, 1);
}
else
{
}
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Activator")
{
CanBePressed = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Activator")
{
CanBePressed = false;
if(!Hit)
{
BeatScroller.instance.BeatMiss();
SR.color = new Color(1f, 0.42f, 0.42f);
Instantiate(MissEffect, new Vector3(0, 0, 0), transform.rotation);
GetComponent<BoxCollider2D>().enabled = false;
}
}
}
}